| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java EE (J2EE) и Spring > Вопрос по Burlap |
| Автор: Zverek 13.11.2006, 15:56 | ||||
| Здравствуйте! Пытаюсь написать web-service c помощью Burlap, не могли бы вы мне помочь со следующей ситуацией: 1) На сервере есть класс наследующийся от BurlapServlet 2) На клиете, с помощью BurlapProxyFactory, получаю прокси-объект:
3) Если ещё раз вызвать
, то он вернёт ссылку на тотже экземпляр прокси-объекта, в то время, как я ожидал, что он создаст на сервере ещё один объект класса "Hello" и даст мне ссылку на связанный с ним прокси. Вопрос: Возможно-ли из клиентcкого приложения создавать новые объекты классов, хранящихся на сервере и получать связанные с ними прокси ? Заранее спасибо за помощь! |
| Автор: tux 13.11.2006, 16:36 |
| Почему на клиенте возвращается один и тот же экземпляр прокси объекта не знаю. Burlap использует для этого стандартный класс java.lang.reflect.Proxy, видимо такова его реализация. На сервер связь такая: один экземпляр сервлета - один серверный объект, следовательно, все зависит от веб-контейнера, он тебе может каждый раз возвращать один и тот же зкземпляр, а может наугад выбирать экземпляр из пула сервлетов. |
| Автор: Zverek 15.11.2006, 15:50 |
| tux, Stampede, Спасибо за подробные объяснения! Действительно, реализовал работу через один экземпляр сервиса и вышло на мой взгяд правильнее. Сервисы использую в качестве посредника, при работе клиентского приложения (на Swing'e) с БД. В моём случае сервисы выполняют следующие функции: 1) Первый сервис - устанавливает соединение с БД, по запросу от клиента, сохраняет Connection в Map'е и возвращает пользователю номер этого Connection. 2) Второй сервис - ему в качестве параметра передаётся номер соединения, строка SQL запроса. Используя их он создаёт объект, обёртку для Statement, также сохраняет его в Map и возвращает уникальный номер клиенту. Также в этом сервисе реализованы методы для получения стандартных типов данных из Resultset'a - getInt, getString и т.д... Stampede, влепил бы тебе плюсик, но пока не могу |
| Автор: tux 15.11.2006, 15:58 |
Будет сделано. |
| Автор: Stampede 16.11.2006, 02:54 | ||
Zverek, я рад, что ты творчески отнесся к предложенным рекомендациям, но мне представляется, что ты стоишь на грани совершения большой архитектурной ошибки, от которой я хотел бы тебя предостеречь. То, что ты делаешь (согласно твоему описанию) называется клиентской демаркацией транзакций. И это не очень хорошая практика. Почему - сейчас расскажу. Тут на самом деле имеют место несколько аспектов, но мы начнем с главного - с собственно транзакций. Значит, ты создаешь на сервере датабазное соединение, присваиваешь ему какой-то номер и далее используешь этот номер из клиента. Возникает один маленький вопрос: а кто и когда будет это соединение закрывать? Сервер по запросу клиента? А в какой момент? По выходу из клиентской проги? А если прога зависла? Или вылетела по исключению? А если клиентский программер забыл добавить вызов закрытия соединения? Ты пойми, что веб-сервис - это штука публичная (даже если и закрытая). К нему нет единого данного на все времена клиента. Для внешнего мира он существует в виде API. И потенциально есть вероятность, что под него будет написано больше одной клиентской программы. И нет никакой гарантии, что клиентский программер будет обязательно продвинутым настолько, чтобы предвидеть все возможные аварийные ситуации. Поэтому идеологически гораздо правильнее делать демаркацию транзакций на стороне сервера. Это означает, один удаленный вызов - одна транзакция. При таком подходе серверный програмист имеет абсолюный контроль над созданием, фиксацией, откатом и закрытием соединений. Ну вот скажи, в чем у тебя такая уж сильная неободимость выполнять все датабазные операции, используя одно и то же соединение? Я больше чем уверен, что для твоего случая найдется более надежное решение, не требующее явного управления транзакциями из клиента. |
| Автор: Zverek 16.11.2006, 14:39 |
| Stampede, Ещё раз большое спасибо, за советы! Да, я действительно задумывался, о том, как закрывать коннект с БД, если допустим клиент потеряет соединение. Сейчас закрываю коннект с БД по выходу из клиентского приложения. Как вариант хочу попробывать использовать HttpSessionBindingListener - создать класс-обёртку для Connection, реализующий этот интерфейс-листенер. При истечении времени ответа сессии HttpSession оповестит мой объект, и он закроет коннект. Вычитал такой способ в книжке Bales "Java Programming with Oracle JDBC". Но это написано применительно к сервлетам, у себя ещё не попробывал. Попробую аргументировать свой выбор: 1) Мое приложение работает следующим образом: в начале работы клиент коннектится к БД (через Веб-сервис), выбирает данные, на их основе создаёт объекты. В процессе дальнейшей работы, при изменении, созданнии или удалении объектов, анналогичные действия выполняются и с записями в БД (т.е. выполняются UPDATE, INSERT, DELETE). НО данные сразу не коммититятся, коммит происходит в тот момент, когда пользователь нажимает кнопку "Сохранить". Если бы у меня коммитилась каждая операция, то действительно для каждой DML операции, можно было бы открывать соединение, и по её выполнении соединение закрывать. 2)Если я правильно понял, то по каждому запросу от клиента, на выполнение DML операции будет открываться отдельное соединение с БД. Не будет ли это сильно нагружать сервер БД и тормозить работу клиента, ведь открытие соединения - операция достаточно трудоёмкая. Пул коннектов тоже не подойдёт т.к. пользователь коннектится к БД под своим именем. Технологию Java начал изучать сравнительно недавно, и если с языком уже более-менне разобрался, то с архитектурой пока опыта не хватает. Поэтому очень интересно узнать мнение профессионалов. |
| Автор: Stampede 16.11.2006, 22:11 | ||
| Zverek, очень хорошие вопросы. Приятно общаться с человеком, которому голова дана не только для того чтоб шапку на ней носить Теперь про твою задачу. То, что ты рассказываешь, делает ситуацию еще хуже! Я-то было подумал, что ты пытаешься сваять толстого удаленного универсального датабазного клиента (типа выполнять команды SQL), а оказывается вон оно что. Ну что ж, давай по порядку. Если точнее, в том порядке, в котором мне это видится. Сразу сообщу хорошую новость: все твои вопросы имеют решения. Просто будь готов к тому, что твои изначальные посылки могут быть не совсем верны. Прежде всего - идея о том, что юзеры должны ходить в базу под своим именем. Чем плохи датабазные средства разграничения прав доступа На самом деле ничем они, конечно, не плохи, но вот использовать и полагаться на них - в данном случае не стОит. Раздельные датабазные логины полностью оправдывали себя в эпоху двухзвенных приложений баз данных - когда гуевый клиент напрямую издавал команды к SQL серверу. В этих условиях другого способа управлять доступом, кроме как устанавливать разным юзерам разные права на уровне объектов СУБД, просто не было в природе. Но вот на авансцену вышли системы массового обслуживания - в первую очередь веб приложения (но и не только!), и ситуация коренным образом изменилась. Теперь о том, чтобы раздавать каждому юзеру по отдельному датабазному логину, не могло быть и речи. Но дело даже не только в этом. Самое главное, возникла необходимость каким-то образом централизовать датабазную логику. Как ответ на это требование, родилась концепция приложения трехзвенной архитектуры: гуевый клиент - сервер приложения - сервер СУБД. Это был исключительно важный момент в истории развития компьютерной мысли. Возможность сосредоточить всю логику по обработке данных в одном месте позволила (теретически) создавать приложения намного более пуленепробиваемые, более надежные, более масштабируемые, более удобные в плане управления и развития. Одним из важнейших следствий такого разделения обязанностей стало то, что теперь клиенту совершенно не нужно было знать о том, где и в каком виде хранятся данные. Это избавило прикладных разработчиков от необходимости кодировать низкоуровневую датабазную логику и позволило им плотнее заняться вопросами собственно пользовательского интерфейса. Так вот, применительно к теме нашего обсуждения, это также означало то, что вся возня с различными датабазными логинами теряла всякий смысл: ведь юзеры теперь ходили в базу не напрямую, а через посредство сервера приложений! Теоретически можно было бы, конечно, предусмотреть несколько разных датабазных ролей по уровню доступа: админы, зарегистрированные юзеры, анонимоусы и пр., и выполнять операции от имени клиента, используя тот или иной предопределенный логин, в зависимости от типа клиента. Но на практике оказалось, что все это не имеет большого смысла, и вот почему. Это только кажется, что встроенная в СУБД система разграничения прав доступа - очень мощная и гибкая. В действительности, и особенно в условиях использования СУБД в рамках трехзвенной архитектуры, при организации контроля доступа возникает масса проблем, для которых датабазная секьюрити не может предложить решения. Например:
Если говоритиь строго, многие из этих вещей таки можно реализовать средствами датабазной секьюрити, но - ценой усложнения структуры самой базы. То есть: база данных и без того вещь непростая - там нужно думать и о производительности, и о конкурентом доступе, и о физическом размещении с учетом требований к масштабированию, копированию/восстановлянию и пр., и об удобстве программного представления, и еще о куче разных вещей. Если мы в эту картину еще введем всякие извороты, которые диктуются логикой управления доступом, то можем запросто вообще за деревьями потерять из виду сам лес. Как ответ на перечисленные сложности и ограничения, передовая энетрпрайзная мысль выработала следующий практический подход: коль скоро нам все равно нужна какая-то система аутентификации (грубо говоря, логин/пароль), и привязанная к ней система авторизации (например, доступ к веб страницам) помимо дотабазной, то не проще ли весь контроль над пользовательскими полномочиями вынести вообще за датабазные скобки? Поэтому в большинстве современных приложений аппсервер ходит в базу под одним и тем же аккаунтом, а для контроля доступа к различным функциональным аспектам приложения использует свою, доморощенную систему разграничения прав. Помимо преимуществ, о которых уже говорилось, это позволяет также без каких-либо проблем использовать единый пул датабазных соединений. Так что будем считать, на вопрос 2) мы ответили. А теперь о главном вопросе:
Ты понимаешь, что ты сделал? Ты сделал классическое двухзвенное приложение, со всеми присущими ему недостатсками, и облачил его в подобие трехзвенного. Но суть-то осталась та же! Чем плоха двухзвенка Самый главный недостаток двухзвенной архитектуры - это то, что операции по доступу к данным со стороны разных клиентов взаимно никак не синхронизированы, и полагаться на помощь в разрешении конфликтов мы можем только на сам сервер СУБД. Что отнюдь не всегда гарантирует нас от ошибок конкурентного доступа. При этом самый худший вид сценария такого рода - это когда пользователю позволяют удерживать открытую транзакцию. Тут у нас открывается самый широкий простор для множественных нежелательных проявлений http://forum.vingrad.ru/topic-105840/0.html#entry805943 (с) мой. Вот навскидку самый простой и совершенно реальный сценарий:
Так вот, юзер Б будет очень долго пялиться в курсор "песочные часы", не имея ни малейшего понятия, что происходит и когда это кончится. Что делать А делать надо очень просто: вот скажи, почему бы тебе не делать коммит после каждой вставки/обновления/удаления? Может, ты хочешь предотвратить возможность просмотра сырых, недоделанных объектов? Типа, сначала сформировать всю накладную, строка за строкой, а потом ее всю целиком сделать доступной? Ну так для этого есть другие решения. Вообще, то, что ты таскаешь по сети эквиваленты ResultSet, говорит для меня о том, что тебе было бы неплохо ознакомиться с энтерпрайзным паттерном http://en.wikipedia.org/wiki/Data_Transfer_Object (он же Value Object). И в заключение главное: чем меньше клиент в распределенной системе знает о каких-то там базах данных, тем лучше и надежнее будет работать вся система в целом, и тем крепче и спокойнее будет сон у ее разработчиков |