Введение
Разумеется, что организовать доступ к базам данных из современного языка программирования в наше время не представляет никакой сложности. Более того, и сами языки программирования более всего оцениваются разработчиками по типу и возможностям заложенных в них средств доступа к базам данных, удобству и полноте интерфейсов. В этом смысле Java не представляет исключения. Уже в версии JDK1.1 появился пакет классов java.sql, обеспечивающий больщинство функций, известных к тому времени разработчикам ODBC-приложений. В этом пакете содержится ряд замечательных классов, например: java.sql.CallableStatement, который обеспечивает выполнение на Java хранимых процедур; java.sql.DatabaseMetaData, который исследует базу данных на предмет ее реляционной полноты и целостности с получением самых разнообразных данных о типах и содержимом таблиц, колонок, индексов, ключей и т.д.; наконец, - java.sql.ResultSetMetaData, с помощью которого можно выводить в удобном виде всю необходимую информацию из таблиц базы данных или печатать сами метаданные в виде названий таблиц и колонок.
Однако, коренное отличие Java от других традиционных языков программирования заключается в том, что одни и те же функции доступа к базам данных, с помощью универсальности и кроссплатформенности Java, можно организовать чрезвычайно гибко, используя все преимущества современных объектно-ориентированных технологий, WWW и Intranet/Internet. Рассмотрим по порядку все варианты использования Java-программ при взаимодействии с базами данных.
1. Java-программы и апплеты с интерфейсом JDBC-ODBC
JDBC (Java Database Connectivity) является не протоколом, а интерфейсом и основан на спецификациях SAG CLI (SQL Access Group Call Level Interface - интерфейс уровня вызова группы доступа SQL).
Сам по себе JDBC работать не может и использует основные абстракции и методы ODBC. Хотя в стандарте JDBC API и предусмотрена возможность работы не только через ODBC, а и через использование прямых линков к базам данных по двух- или трех-звенной схеме (см. Рис.1), эту схему используют гораздо реже, чем повсеместно используемый JDBC-ODBC-Bridge занимающий центральное место в общей схеме взаимодействия интерфейсов (см. Рис. 2)
Рис. 1. Непосредственный доступ к базе данных по 3-х-звенной схеме.
Рис. 2. Схема взаимодействия интерфейсов.
Даже беглого взгляда на Рис. 2 вполне достаточно, чтобы понять - общая схема взаимодействия интерфейсов в Java удивительным образом напоминает столь всем знакомую схему ODBC с ее гениальным изобретением драйвер-менеджера к различным СУБД и единого универсального пользовательского интерфейса. JDBC Driver Manager - это основной ствол JDBC-архитектуры. Его первичные функции очень просты - соединить Java-программу и соответствующий JDBC драйвер и затем выйти из игры. Естественно, что ODBC был взят в качестве основы JDBC из-за его популярности среди независимых поставщиков программного обеспечения и пользователей. Но тогда возникает законный вопрос - а зачем вообще нужен JDBC и не легче ли было организовать интерфейсный доступ к ODBC-драйверам непосредственно из Java? Ответом на этот вопрос может быть только однозначное нет. Путь через JDBC-ODBC-Bridge, как ни странно, может оказаться гораздо короче.
1. ODBC нельзя использовать непосредственно из Java, поскольку он основан на C-интерфейсе. Вызов из Java C-кода нарушает целостную концепцию Java, пробивает брешь в защите и делает программу трудно-переносимой.
2. Перенос ODBC C-API в Java-API нежелателен. К примеру, Java не имеет указателей, в то время как в ODBC они используются.
3. ODBC слишком сложен для понимания. В нем смешаны простые и сложные вещи, причем сложные опции иногда применяются для самых простых запросов.
4. Java-API необходим, чтобы добиться абсолютно чистых Java решений. Когда ODBC используется, то ODBC-драйвер и ODBC менеджер должны быть инсталлированы на каждой клиентской машине. В то же время, JDBC драйвер написан полностью на Java и может быть легко переносим на любые платформы от сетевых компьютеров до мэйнфреймов.
JDBC API - это естественный Java-интерфейс к базовым SQL абстракциям и, восприняв дух и основные абстракции концепции ODBC, он реализован, все-таки, как настоящий Java-интерфейс, согласующийся с остальными частями системы Java.
В отличие от интерфейса ODBC, JDBC организован намного проще. Главной его частью является драйвер, поставляемый фирмой JavaSoft для доступа из JDBC к источникам данных. Этот драйвер является самым верхним в иерархии классов JDBC и называется DriverManager. Согласно, установившимся правилам Internet, база данных и средства ее обслуживания идентифируются при помощи URL.
jdbc:где под
В некоторых случаях вместо ODBC может быть использовано имя прямого сетевого сервиса к базе данных, например:
jdbc:dcenaming:accounts-payable,или
jdbc:dbnet://ultra1:1789/state
В последнем случае часть URL //ultra1:1789/state представляет собой
Однако, как уже говорилось выше, чаще всего, все-таки используется механизм ODBC благодаря его универсальности и доступности. Программа взаимодействия между драйвером JDBC и ODBC разработана фирмой JavaSoft в сотрудничестве с InterSolv и называется JDBC-ODBC-Bridge. Она реализована в виде JdbcOdbc.class (для платформы Windows JdbcOdbc.dll) и входит в поставку JDK1.1. Помимо JdbcOdbc-библиотек должны существовать специальные драйвера (библиотеки), которые реализуют непосредственный доступ к базам данных через стандартный интерфейс ODBC. Как правило эти библиотеки описываются в файле ODBC.INI. На внутреннем уровне JDBC-ODBC-Bridge отображает медоды Java в вызовы ODBC и тем самым позволяет использовать любые существующие драйверы ODBC, которых к настоящему времени накоплено в изобилии.
Рассмотрим типичное приложение на Java c доступом к типичному реляционному серверу или даже к обычной dBase-таблице.
// Следующий код на Java используется как пример. Простой подстановкой // соответствующих значений url, login, и password, и, затем подстановкой // SQL операторов вы можете посылать их в базу данных. //-------------------------------------- // // Module: SimpleSelect.java // // Описание: Эта программа для ODBC API интерфейса. Java-приложение // будет присоединяться к JDBC драйверу, посылать select оператор // и показывать результаты в таблице // // Продукт: JDBC к ODBC Мост // // Автор: Karl Moss (С.Дунаев модификация для работы с кириллицей) // // Дата: Апрель 1997 // // Copyright: 1990-1996 INTERSOLV, Inc. // This software contains confidential and proprietary // information of INTERSOLV, Inc. //-------------------------------------- import java.net.URL; import java.sql.*; import java.io.*; class SimpleSelect { public static void main (String args[]) { String url = «jdbc:odbc:dBase»; String query = «SELECT * FROM my_table»; try { // Загрузка jdbc-odbc-bridge драйвера Class.forName («sun.jdbc.odbc.JdbcOdbcDriver»); DriverManager.setLogStream(System.out); // Попытка соединения с драйвером. Каждый из // зарегистрированных драйверов будет загружаться, пока // не будет найден тот, который сможет обработать этот URL Connection con = DriverManager.getConnection ( url, «», «»); // Если не можете соединиться, то произойдет exception // (исключительная ситуация). Однако, если вы попадете // в следующую строку программы, значит вы успешно соединились с URL // Проверки и печать сообщения об успешном соединении // checkForWarning (con.getWarnings ()); // Получить DatabaseMetaData объект и показать // информацию о соединении DatabaseMetaData dma = con.getMetaData (); //System.out.println(«\nConnected to « + dma.getURL()); //System.out.println(«Driver « + //dma.getDriverName()); //System.out.println(«Version « + //dma.getDriverVersion()); //System.out.println(«»); // Создать Оператор-объект для посылки // SQL операторов в драйвер Statement stmt = con.createStatement (); // Образовать запрос, путем создания ResultSet объекта ResultSet rs = stmt.executeQuery (query); // Показать все колонки и ряды из набора результатов dispResultSet (rs); // Закрыть результирующий набор rs.close(); // Закрыть оператор stmt.close(); // Закрыть соединение con.close(); } catch (SQLException ex) { // Случилось SQLException. Перехватим и // покажем информацию об ошибке. Заметим, что это // может быть множество ошибок, связанных вместе // //System.out.println («\n*** SQLException caught ***\n»); while (ex != null) { //System.out.println («SQLState: « + // ex.getSQLState ()); //System.out.println («Message: « + ex.getMessage ()); //System.out.println («Vendor: « + //ex.getErrorCode ()); ex = ex.getNextException (); //System.out.println («»); } } catch (java.lang.Exception ex) { // Получив некоторые другие типы exception, распечатаем их. ex.printStackTrace (); } } //---------------------------------- // checkForWarning // Проверка и распечатка предупреждений. Возврат true если // предупреждение существует //---------------------------------- private static boolean checkForWarning (SQLWarning warn) throws SQLException { boolean rc = false; // Если SQLWarning объект был получен, показать // предупреждающее сообщение. if (warn != null) { System.out.println («\n *** Warning ***\n»); rc = true; while (warn != null) { //System.out.println («SQLState: « + //warn.getSQLState ()); //System.out.println («Message: « + //warn.getMessage ()); //System.out.println («Vendor: « + //warn.getErrorCode ()); //System.out.println («»); warn = warn.getNextWarning (); } } return rc; } //---------------------------------- // dispResultSet // Показать таблицу полученных результатов //---------------------------------- private static void dispResultSet (ResultSet rs) throws SQLException, IOException { // Объявление необходимых переменных и // константы для желаемой таблицы перекодировки данных int i, length, j; String cp1 = new String(«Cp1251»); // Получить the ResultSetMetaData. Они будут использованы // для печати заголовков ResultSetMetaData rsmd = rs.getMetaData (); // Получить номер столбца в результирующем наборе int numCols = rsmd.getColumnCount (); // Показать заголовок столбца for (i=1; i<=numCols; i++) { if (i > 1) System.out.print(«,»); //System.out.print(rsmd.getColumnLabel(i)); } System.out.println(«»); // Показать данные, загружая их до тех пор, пока не исчерпается // результирующий набор boolean more = rs.next (); while (more) { // Цикл по столбцам for (i=1; i<=numCols; i++) { // Следующая группа операторов реализует функции перекодировки // строк из таблицы базы данных в желаемый формат, потому что в // различных базах символы могут быть закодированы произвольным // образом. Если использовать стандартный метод - getString - на выходе // получается абракадабра. Строки нужно сначала перевести в Unicode, // затем конвертировать в строку Windows и убрать лидирующие нули InputStream str1 = rs.getUnicodeStream(i); byte str2[]; byte str3[]; int sizeCol = rsmd.getColumnDisplaySize(i); str2 = new byte[sizeCol+sizeCol]; str3 = new byte[sizeCol+sizeCol]; length = str1.read(str2); // Здесь нужно убрать нули из строки, которые предваряют каждый // перекодированный символ k=1; for (j=1; jВ этой простой программе, приводимой во множестве руководств, мною произведено одно небольшое изменение, позволяющее использовать ее для работы с различными базами данных, содержащих таблицы с полями в кириллической кодировке. Дело в том, что хотя Java автоматически производит преобразования из Unicode и обратно в соответствии с установленными на вашей машине языковыми спецификациями (так называемые locale), эти преобразования не всегда действуют по отношению к кириллическим фонтам, особенно, когда кириллические строки прописаны не непосредственно в Java-программе, а передаются из внешних источников, например из баз данных через несколько промежуточных слоев. Та же проблема, как мы увидим далее, возникает и при использовании сервлетов, работающих в тесной взаимоувязке с Web-серверами.
2. Спец-ориентированные Java-приложения
2.1 RMI-приложения
Вызов удаленных методов (RMI - Remote Method Invocation) обеспечивает средства коммуникации между Java программами, даже если они выполняются на разных компьютерах, находящихся в противоположных точках земного шара.
Важная особенность RMI заключается в том, что он представляет программируемый интерфейс для работы с сетями в отличие от сокетов TCP. Главное преимущество его в том, что он предлагает вам интерфейс более высокого уровеня, основанный на вызовах методов, так, как если бы удаленный объект обрабатывался локально. RMI более удобен и более естественен, чем интерфейс, основанный на сокетах, но он требует выполнения Java-программ на обоих концах соединения. Сетевое соединение, тем не менеее, достигается использованием все того же TCP/IP протокола.
Рассмотрим основные шаги для построения работающего RMI-приложения:
- Разработка удаленных объектов и кодов для сервера и клиента
- Java-компиляция
- RMI-компиляция
- Перемещение .class-файлов в соответствующие директории
- Регистрация
- Старт сервера
- Старт клиента
Следующий ниже пример разработан для клиента и сервера, выполняющихся на одной и той же машине, однако, заложенные здесь принципы можно с успехом применить для программ, выполняющихся на разных машинах и даже с различной архитектурой. Программа клиента ищет имена и номера телефонов в центральной базе, постоянно резидентной на сервере.
Как работает RMI
Вы определяете Java-интерфейс, чтобы описать каждый объект, который будет дистанционно разделяем, и перечисляeте общие методы, которые могут быть вызваны для объекта. Сервер будет использовать RMI-интерфейс и создаст объекты для вызова, специальным образом зарегистрированные и доступные для вызова по URL-основанной схеме, например:
rmi://localhost/LookupServerКлиент, используя эту запись, будет пытаться отыскать объект с данным именем, и получить удаленную ссылку к нему. Затем вызванный метод будет обработан с помощью RMI компилятора и преобразован из пользовательского кода в последовательную форму объекта, который передается пользователю с помощью TCP/IP.
Разработка удаленного объектного кода
RMI поддерживает объекты Java, осуществляющие связь через их методы, независимо от того, где эти объекты размещены. Первый шаг для создания класса, к которому можно обратиться дистанционно, - это определение интерфейса, описывающего методы, которые являются дистанционно-разделяемыми.
// Lookup.java import java.rmi.*; public interface Lookup extends Remote { public String findInfo(String info) throws RemoteException; }Java.rmi.Remote - пустой интерфейс, который указывает, что это удаленный объект, - так объекты класса, выполняющие Поиск(«Lookup») отмечены удаленными ссылками. Все методы в удаленном интерфейсе должны быть объявлены через исключение типа Java.rmi.RemoteException, которое активизируется всякий раз, когда метод удаленного вызова дает сбои.
Разработка серверного кода
После того как вы определили интерфейс к удаленному объекту, нужно выполнить следующий шаг - разработать код сервера. Сервер осуществляет объектный интерфейс и создает образцы объекта, который будет дистанционно-разделяем.
Для нашего примера это будет выглядеть следующим образом:
// LookupServer.java import java.io.*; import java.util.*; import java.rmi.*; import java.rmi.server.*; public class LookupServer extends UnicastRemoteObject implements Lookup { private Vector save = new Vector(); public LookupServer(String db) throws RemoteException { try { FileReader fr = new FileReader(db); BufferedReader br = new BufferedReader(fr); String s = null; while ((s = br.readLine()) != null) save.addElement(s); fr.close(); } catch (Throwable e) { System.err.println(«exception»); System.exit(1); } } public String findInfo(String info) { if (info == null) return null; info = info.toLowerCase(); int n = save.size(); for (int i = 0; i < n; i++) { String dbs = (String)save.elementAt(i); if (dbs.toLowerCase().indexOf(info) != -1) return dbs; } return null; } public static void main(String args[]) { try { RMISecurityManager security = new RMISecurityManager(); System.setSecurityManager(security); String db = args[0]; LookupServer server = new LookupServer(db); Naming.rebind(«LookupServer», server); System.err.println(«LookupServer ready...»); } catch (Throwable e) { System.err.println(«exception: « + e); System.exit(1); } } }Сервер читает в текстовой базе номера телефонов и имена и сохраняет их внутренне. Метод findInfo ищет затем нужное имя и телефон.
Пример базы данных:
Ivanov, Ivan 295-0083
Petrov, Peter 775-9958
Romanov, Alexander 555-7779Заметим, что LookupServer является расширением стандартного класса java.rmi.server.UnicastRemoteObject и выполняет Lookup. Один из этих классов обеспечивает некоторые базисные реквизиты, необходимые для удаленных объектов, а другой определяет методы, которые будут вызваны дистанционно.
Установка службы безопасности
Наиболее сложная часть этого кода - то, что происходит в процедуре main(). Первым делом нужно установить защиту. RMI принужден загружать удаленные .class файлы и в этом смысле напоминает какой-нибудь web-браузер с его операциями по загрузке апплетов, что само по себе всегда небезопасно. Если вы не установили защиту, то по умолчанию должны загружаться только локальные файлы, и RMI по определению не может работать с такими ограничениями. Так что вы должны установить security manager, чтобы сделать возможной загрузку удаленных .class файлов.
Образец LookupServer затем регистрируется с помощью службы Naming.rebind и становится доступным клиенту по имени.
Вы могли бы задаться вопросом, как удаленный метод фактически становится вызываемым, если сервер не содержит никакого сетевого кода и никаких TCP/IP примитивов? Это происходит за сценой, поскольку сервер и клиент используют так называемые скелетоны и стабы для коммуникации между собой. Соответствующие .class файлы генерируются из серверного .class файла через RMI транслятор, описанный ниже.
Концептуально, класс stub(заглушка) выглядит так:
public class LookupServer_Stub extends java.rmi.server.RemoteStub implements Lookup, java.rmi.Remote { ... }и скелетон - так:
public class LookupServer_Skel implements java.rmi.server.Skeleton { ... }Использование команды:
Javap -c LookupServer_Stub
будет показывать байт-код и иллюстрировать то, что происходит за сценой.Стаб(stub) -это суррогат для удаленного объекта, и скелетон - некая сущность на сервере, которая обрабатывает удаленные вызовы.
Стаб обеспечивает функции приема передачи на стороне клиента, а скелетон - на стороне сервера. При этом производится преобразование объектов в последовательную форму, а проще говоря, в поток байтов, передаваемых с помощью протокола TCP/IP
Разработка клиентского кода
// LookupClient.java import java.rmi.*; import java.rmi.server.*; public class LookupClient { public static void main(String args[]) { try { RMISecurityManager security = new RMISecurityManager(); System.setSecurityManager(security); String host = «localhost»; String server = «LookupServer»; String name = «rmi://» + host + «/» + server; Lookup look_obj = (Lookup)Naming.lookup(name); String results = look_obj.findInfo(args[0]); if (results == null) System.err.println(«** not found **»); else System.out.println(results); } catch (Throwable e) { System.err.println(«exception: « + e); System.exit(1); } } }Если вы активизируете сервер, выполняя прямой клиентский запрос, то защита для пользователя определяется такая же как и на сервере. URL при этом определяется как:
Rmi://localhost/LookupServer
где localhost - имя локального компьютера (IP, адрес = 127.0.0.1), используемого как сервер. Клиент располагается на той же самой машине. Вы можете также использовать и удаленную главную ЭВМ. Когда вызов к нужному методу сделан, результаты незамедлительно передаются клиенту.Компиляция кода
Три файла - Lookup.java, LookupServer.java, и LookupClient.java компилируются как и обычно в Java:
javac Lookup.java javac LookupServer.java javac LookupClient.javaВыполнение RMI компилятора
После того, как вы откомпилируете эти файлы, выполните RMI Compiler (rmic):
rmic LookupServer
чтобы получить LookupServer_Skel.class и LookupServer_Stub.class файлы.Перемещение.class файлов в соответствующее им место на диске
Вы должны переместить клиентские файлы (Lookup.class, LookupClient.class, и LookupServer_Stub.class) в директорию, откуда вы их желаете выполнять как клиент.
Вы должны переместить серверные файлы (Lookup.class, LookupServer.class, LookupServer_Skel.class, и LookupServer_Stub.class) в директорию, где они станут доступными для публичного доступа.
Регистрация
Объект, к которому обращаются дистанционно, должен быть введен в регистр объектов, т.е. зарегистрирован. В JDK 1.1 имеется специальная программа
RmiregistryRmiregistry может выполняться или в отдельном окне или как фоновый процесс на сервере.
Старт сервера
Вы должны стартовать сервер по команде:
java LookupServer database_nameК примеру, если ваша база данных с набором имен и телефонов находится в файле C:\PHONE.TXT, то вы должны дать команду:
java LookupServer C:\PHONE.TXTСтарт клиента
Клиентская программа стартуется по команде:
java LookupClient Ivanov«Ivanov» - это искомое имя в базе.
Таким образом, RMI дает возможность создавать распределенные Java-to-Java прикладные программы, в которых методы удаленных объектов Java вызываются из других Java-программ на различных главных ЭВМ так как, если бы эти методы вызывались локально. Естественно, что подобные возможности можно эффективно использовать при работе с SQL-серверами, которые предоставляют Java-API интерфейс для доступа к данным. Например, в группе продуктов Informix в Informix Client SDK дано описание Informix Object Interface for Java, где приводятся многочисленные примеры, как организовать взаимодействующие RMI-приложения с доступом к базам данных Informix. Более того, имеется и соответствующий RMI-сервер, который содержит массу удобных и полезных методов, которые можно вызывать дистанционно. В сущности, приведенный выше пример можно приспособить для работы с любыми базами данных и SQL-серверами, если вы знаете каким образом устроен Java-API интерфейс для доступа к базам данных. В крайнем случае не возбраняется и использование JDBC в RMI-приложениях, хотя вряд ли это будет в достаточной степени эффективно. В Informix, например, для непосредственного взаимодействия с базами данных существуют два RMI-пакета: informix.api.remote.rmi - для удаленных клиентов и informix.api.remote.rmi.server - для rmi-сервера. При этом в клиентском приложении используется интерфейс к DBMSManager, который накапливает информацию обо всех серверах Informix и базах данных, и вы можете установить либо локальное, либо удаленное соединение с базой через RMI сервер. Для локального соединения создается DirectDBMSManager объект. Для удаленного соединения создается RMIDBMS-Manager объект и передается к соответствующему RMI серверу. Спецификация RMI сервера осуществляется в форме:
rmi://hostname[:port]/ //Создание DBMSManager объекта DBMSManager getDBMSManager() throws Exception { // based on RMI Checkbox, get appropriate DBMSManager DBMSManager dbmsManager; if (RMIcheckbox.getState()) dbmsManager = new RMIDBMSManager(rmiServerTextField.getText()); else dbmsManager = new DirectDBMSManager(); return dbmsManager;Само взаимодействие с базой осуществляется с помощью специальных транзакционных методов и методов управления курсором, которые могут вызываться как локально, так и дистанционно. В различных базах и SQL-серверах это может осуществляться по разному, но если сервер кроссплатформенный - единый и универсальный Java-API интерфейс играет немаловажную роль.
2.1 Java, инкапсулированная в СУБД
Java может быть встроена в СУБД множеством различных способов и при этом всегда достигается решение сразу нескольких задач:
- Применение полноценного языка программирования для написания хранимых процедур. В сервер встраивается виртуальная машина Java и внутренний интерфейс JDBC. Встраивание мощного и безопасного языка программирования позволяет обойти ограничения хранимых процедур на языке SQL.
- Расширение объектных типов данных. Объекты, написанные на языке Java могут храниться в виде значений в реляционной таблице. Это позволяет создавать и использовать произвольные типы данных. Java-класс может быть использован в качестве типа данных для столбца таблицы. Каждое поле такой записи становится экземпляром соответствующего Java-класса. В качестве примера возьмем простой класс на языке Java, хранящий адреса. Прикладной код может быть включен в методы класса. Например, строковые данные могут содержаться только в полях Street и Postal Code(почтовый код), а значение поля City может вычисляться на основе почтового индекса. Более того, можно использовать наследование классов и методов. При этом допускается перегрузка методов в зависимости от используемого класса. Так можно создать классы US_Address и Rus_Address, унаследованные от базового класса Address. В каждом таком классе могут быть разные методы определения городов по почтовому индексу и методы проверки его значений, естественно разные для России и США. Даже сам SQL может использоваться для доступа к Java-объектам, как это уже сделано в Sybase Adaptive Server.
Следующий пример вставляет новую запись в таблицу:
INSERT INTO employees (id, name, Address) VALUES(1789, «Serg Dunaev», new Rus_Address(«58 Gagarin Street», «153038») )Следующий запрос с использованием Java-объектов возвращает список сотрудников, живущих на указанной улице:
SELECT name FROM employees WHERE Rus_Address.street="Gagarin Street"
- Единая программная модель. Впервые прикладные программные компоненты можно будет перемещать между клиентскими программами, серверами приложений и СУБД. Разработчики смогут использовать единую программную модель на всех уровнях информационной системы. Произвольная программа на Java состоит из набора классов. Для их использования необходимо провести инсталляцию Java-классов в СУБД. Классы должны быть откомпилированы в байт-код и тем самым, готовы для использования в любой виртуальной машине. В связи с использованием виртуальной машины для исполнения методов на Java и встроенной поддержке интерфейса JDBC, один и тот же объект на языке Java может быть использован как внутри, так и вне СУБД.
3. Java-сервлеты
Если апплеты расширяют функциональность Web-браузеров, то сервлеты расширяют функциональность Web-серверов и являются мощным средством программирования. В последнее время многие предпочитают обыкновенным апплетам, загружаемым локально или удаленно, именно сервлеты, которые не нужно никуда загружать и, которые всегда выполняются в контексте Web-сервера, обеспечивая, в отличие от обычных CGI-процессов или скриптов, куда более развитые возможности.
3.1 Сервлеты на базе Java Web Server
Сервлеты являются объектами, которые образуют специфический интерфейс, с помощью которого их можно свободно встраивать в Java-ориентированные Web-сервера. В отличие от апплетов, они являются облегченными Java-объектами (без графики и GUI-компонентов), но зато, будучи кроссплатформенными и динамически загружаемыми и, используя все возможности Java, они могут использовать практически любой HTML и взаимодействовать с любым браузером. Естественно, что сервлеты можно использовать и используют для доступа к базам данных, программируя все удаленные операции с базой из браузера. В настоящее время большинство Web серверов поддерживают технологию сервлетов. Java Web-Server хорош тем, что что он наиболее полно реализует эту технологию. Стандартный Servlet-API реализован как в самом Java Web Server, так и в отдельном продукте JSDK, но начиная с версии 1.2, он включен в состав JDK. Характерной особенностью сервлетов является то, что они не требуют создания новых процессов при каждом новом запросе. Множество сервлетов выполняются параллельно в рамках одного процесса на сервере и по своей производительности превосходят как CGI, так и Fast-CGI приложения (см. Рис.3).
Рис. 3. Возможные процессы на Web-сервере.Несмотря на то, что сервлеты используют HTTP-протокол, им нет необходимости перезагружать процесс при каждом новом запросе и это также повышает их быстродействие. К сожалению, когда вы используете сервлеты, опять-таки, возникает проблема использования кириллических символов. Дело в том, что стандартный путь прохождения данных тут не действует, потому что Web-сервер при старте запускает так называемый Java-handler, которому передает на выполнение все Java-сервлеты и обменивается вводом-выводом именно с ним через специальную библиотеку классов. Поэтому, если вы хотите получать в браузере от вашего Web-сервера, который исполняет сервлеты, кириллические строки, вам надо использовать вместо стандартных примитивов ввода/вывода некоторые специальные методы для работы со строками, например:
// Определение необходимого кодификатора // в зависимости от используемого типа кодировки String dos = new String(«Cp866»); String win = new String(«Cp1251»); String iso = new String(«8859_5»); String im = new String(«Кириллический текст»); ....................................................... out.println(«»); //out.write(im.getBytes(dos)); out.write(im1.getBytes(win)); //out.write(im2.getBytes(iso)); out.println(«