IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  SOA и Web-сервисы  >

Управление HTTP-сервером с помощью REST-интерфейсов, Project Zero и WebSphere sMash

Создание REST-интерфейса на основе Zero для httpd

developerWorks
Опции документа

Опции документа, требующие включения JavaScript, не отображаются

Обсудить

Исходные тексты примера


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: средний

Ден Джемиоло, программист-консультант, IBM

06.11.2009

Пользователи WS-* и REST продолжают спорить о том, какие методы наиболее подходят для решения тех или иных проблем, причем пользователи WS-* часто утверждают, что более сложные проблемы уровня предприятия нельзя решить только с помощью REST. В данной статье мы проверяем обоснованность этого утверждения, разрабатывая REST-решение для группы проблем, о которой пользователи REST говорят нечасто: управление системами. В ранее написанном руководстве developerWorks я показывал, как создать интерфейс для Web-сервиса, который управляет HTTP-серверами; в том руководстве средствами стандартов WSDL и WS-* определялся интерфейс управления, и с помощью программного обеспечения Apache Muse и Apache Axis создавалось приложение для управления. В сегодняшней статье я на основе принципов проектирования из Project Zero и REST воссоздаю интерфейс и функции первоначального приложения и выясняю, подойдет ли REST для данного корпоративного проекта.

Примечание редактора: IBM® WebSphere® sMash и IBM WebSphere sMash Developer Edition основаны на получившем широкую известность проекте инкубатора Project Zero. Project Zero – это сообщество разработчиков WebSphere sMash, которое развивает бесплатную платформу разработки приложений, предлагая новые версии, дополнительные функции и экспертную поддержку.

Предварительные требования

Эта статья предполагает, что вы загрузили Project Zero и либо уже изучили руководство для начинающих, либо даже сами создали простое приложение. Также необходимо знакомство с руководством developerWorks Создание WSDM-интерфейса для HTTP-сервера с помощью Apache Muse, в котором рассказано, как создать управляющий интерфейс для HTTP-сервера, используя спецификации Apache Muse и WS-*. Чтение данной статьи не требует профессионального владения Muse или WS-*, нужно только понимать проблему, которая решалась в вышеназванном руководстве и какие функции обеспечивал приведенный там код.

Введение

Мы не собираемся перечислять аргументы ни в пользу, ни против выбора технологии WS-* или REST, и не будем определять, какой подход лучше. Цель данной статьи – показать, насколько плодотворную альтернативу предоставляют методы разработки REST и Web 2.0 для проектов по управлению системами, и предложить разработчикам дополнительные варианты. Пользователи WS-* и REST давно спорят о том, какой метод наиболее подходит для тех или иных групп проблем, при этом пользователи WS-* часто говорят, что сложные проблемы корпоративного уровня нельзя решить с помощью REST.

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

Повторение: наш интерфейс WSDM для HTTP-серверов

В исходном интерфейсе Web-сервисов, который мы создали для управления HTTP-серверами, для создания набора свойств и операцийиспользовался документ WSDL 1.1. Эти свойства – пары имя-значение, доступ к которым можно получить с помощью операций, определенных в спецификации WS-ResourceProperties (WS-RP). Некоторые из использованных свойств были определены спецификациями WS-* (например, WS-DistributedManagement или WSDM), а другие относились к ресурсам нашего HTTP-сервера. Далее приводим полный список использованных нами свойств:

  • ResourceId (WSDM)
  • ManageabilityCapability (WSDM)
  • Description (WSDM)
  • Caption (WSDM)
  • Version (WSDM)
  • OperationalStatus (WSDM)
  • Name (специальное)
  • Port (специальное)
  • ThreadsPerChild (специальное)

Операции, включенные в наш интерфейс, были взяты из WS-RP и занимались чтением или записью одного или нескольких значений свойств. Также у нас были две специальные операции, Start и Stop, которые использовались соответственно для запуска и остановки HTTP-серверов. Все операции WS-RP предоставлялись средой Apache Muse, а специальные операции реализовывались Java-кодом™ и включали точки расширения Apache Muse. Далее приводим полный список использованных нами операций:

  • GetResourceProperty (WS-RP)
  • GetResourcePropertyDocument (WS-RP)
  • GetMultipleResourceProperties (WS-RP)
  • Start (специальная)
  • Stop (специальная)

Возможно, вы помните, что мы не включили ни одну из операций записи WS-RP, потому что не было свойств, которые мы хотели бы изменить непосредственно. Некоторые из этих свойств были изменяемыми, но только вследствие побочного эффекта (например, свойство OperationalStatus могло меняться при запуске или остановке сервера).

Преобразование WS-ResourceProperties в REST

Программирование REST основывается на правилах и соглашениях уровня сетевого транспорта (как правило, HTTP) и нескольких хорошо известных форматах данных (обычно XML или JSON). Для обеспечения доступа к информации в документе свойств нашего ресурса средствами REST мы воспользуемся методом HTTP GET и получим JSON-объект с полями (парами имя-значение), аналогичными свойствам нашего ресурса. Иначе говоря, мы заменим документ со свойствами ресурса JSON-объектом и вместо методов WS-RP используем HTTP-методы. Листинг 1 показывает, как может выглядеть JSON-представление ресурса нашего HTTP-сервера.


Листинг 1. JSON-представление HTTP-сервера
                
{
  "name": "server1.ibm.com", 
  "port": 8080, 
  "threads": 250, 
  "admin": "admin@us.ibm.com"
}
      

Возможно, вы заметили, что в нашем JSON-представлении отсутствуют свойства WSDM. Некоторые концепты WS-* не переводятся в REST-проекте или находятся в других областях этого приложения. Например, свойства WSDM ResourceId и соответствующий шаблон ресурса не требуются; вместо того, чтобы определять ресурсы с помощью ссылок на конечную точку и значение ResourceId, мы будем пользоваться шаблонами URI и сделаем так, чтобы у каждого ресурса был свой URI. Использование HTTP для части интерфейса нашего ресурса исключает потребность в некоторых из наших ресурсов и операций (пользователи WS-* могут сказать, что мы также ограничиваем себя протоколом HTTP, но вопрос о том, хорош он или нет, остается за пределами нашей статьи). Мы также будем использовать описательные URI и предоставлять удобочитаемую документацию наших сервисов, в отличие от некоторых описательных свойств, которые были встроены в WSDL-документ. Практический результат состоит в том, что наше JSON-представление обладает достаточной для нас полнотой, с учётом того, что теперь мы получаем решение, ориентированное на HTTP.

Чтобы облегчить обнаружение и использование ресурсов нашего HTTP-сервера, мы воспользуемся URI-шаблоном кода /httpd/{serverName} – он будет их дифференцировать. URI-код /httpd будет ссылаться на всю группу HTTP-серверов, управляемых данным приложением, а URI-код /httpd/{serverName} будет ссылаться на индивидуальный HTTP-сервер. Запросы, посылаемые на URI группы, будут сопоставляться со всей группой (когда это возможно), а запросы на URI индивидуального сервера будут ограничены тем сервером, который им представлен. В таблице 1 показаны интерфейсы REST API, которые будут описывать ресурсы нашего HTTP-сервера.


Таблица 1. REST API для ресурсов HTTP-сервера
МетодURIФорматРезультат
GET/httpdtext/jsonСписок имён серверов под управлением данного приложения.
GET/httpd/{serverName}text/jsonJSON-объект, содержащий сведения о конфигурации конкретного сервера.

Если бы нам захотелось расширить наш интерфейс так, чтобы и пользователи могли непосредственно изменять эти свойства, мы бы скорее воспользовались методом PUT в HTTP, а не SetResourceProperties в WS-RP. Шаблоны URI для PUT-запросов были бы те же, что и для GET, с единственной серьёзной разницей в том, что PUT-запросы отправляют JSON-данные, а GET-запросы получают их.

Преобразование специальных операций в REST

Специальные операции Start и Stop значительно сложнее для проектировщиков. У нас имеется только четыре HTTP-метода (GET, PUT, POST и DELETE), а встраивание названий операций в PUT- или POST-запросы считается в REST дурным тоном. Чтобы разрешить эту проблему, будем рассматривать запуск и остановку сервера под иным углом. Представим себе, что это не просто действие по отношению к определенному HTTP-серверу, а соответственно, создание или уничтожение процесса HTTP-сервера. Это не так уж сложно, потому что ваша операционная система именно так и обрабатывает запросы на запуск и остановку: она создает процесс, который подлежит мониторингу и, в конечном итоге, остановке. Мы создадим ресурс процесса HTTP-сервера с собственным набором URI и собственной HTTP-семантикой. В таблице 2 показаны REST API для этого нового типа ресурса.


Таблица 2. REST API для ресурсов HTTP-процессов
МетодURIФорматРезультат
GET/httpd/{serverName}/processОтсутствует200 OK, если сервер работает, 404 Not Found, если нет.
POST/httpd/{serverName}/processОтсутствуетЗапускает сервер и выдает сообщение 201 Created.
DELETE/httpd/{serverName}/processОтсутствуетОстанавливает сервер и выдает сообщение 204 No Content.

Как видите, ресурс HTTP-процесса не имеет JSON-представления, это – просто URI. Отправка HTTP POST на этот URI сгенерирует процесс запуска сервера, HTTP DELETE уничтожит этот процесс, а HTTP GET выдаст информацию по статусу (как свойство OperationalStatus из WSDM, которое нам больше не нужно). Разделение нашего интерфейса на два отдельных типа ресурсов позволяет нам получить аналогичную функцию, не прибегая к методам в стиле RPC или WS-*. В следующем разделе мы увидим, как перевести это проектное решение в настоящий работающий код.

Реализация управления HTTP-сервером с помощью Zero

Написание кода для реализации REST-интерфейсов, которые мы создали в предыдущем разделе, будет достаточно простым, особенно если вы поняли реализацию на основе Muse из предыдущего руководства. В нашем проекте вместо Java-классов мы будем использовать скрипты, вместо WSDL-файлов – комментарии RESTdoc, а вместо XML – JSON.

Доступ к HTTP-серверу Apache с помощью Groovy

Прежде чем приступить к написанию кода, нужно удостовериться, что HTTP-сервер Apache (httpd) установлен и нормально работает. Если вы не проработали до конца предыдущее руководство и не установили httpd, в разделе Ресурсы вы найдете ссылку на Web-сайт Apache, с него можно скачать дистрибутив сервера и инструкции по его установке. Убедитесь в том, что вы установили сервер в качестве сервиса (или демона); если вы в этом не уверены, просто введите в командной строке httpd -k install, и если сервис еще не существует, он будет создан. Установка httpd в качестве сервиса позволит нам асинхронно запускать и останавливать сервер из нашего кода.

Установив httpd, нужно создать проект Zero для хранения нашего кода. Используйте команду из листинга 2 для создания проекта с названием zero.httpd; по умолчанию этот проект будет содержать зависимости от Zero Core и инструмента RESTdoc, а это все, что нам нужно для кода, который мы напишем в рамках этой статьи. Если вы хотите пойти дальше и увидеть завершенный проект, можете скачать его из раздела Загрузка.


Листинг 2. Создание примера проекта
                
$ zero create zero.httpd
      

Прежде чем писать код, нужно решить, как этот код будет находить места установки httpd, чтобы читать из них файлы и выполнять соответствующие команды. В предыдущем руководстве мы использовали файл muse.xml для создания параметров инициализации, содержащих пути к месту установки httpd. Далее мы могли создавать пути к httpd-файлам, не прибегая к жёстко закодированным установочным данным в наших исходных Java-файлах. Используем аналогичный прием в Zero, на этот раз мы с помощью файла zero.config создадим карту преобразования серверных имен в установочные директории; имя сервера может содержать любые буквы и цифры и будет использоваться для создания конкретных URI для ресурсов HTTP-сервера. В листинге 3 показан пример файла zero.config, который может послужить вам основой.


Листинг 3. Конфигурирование мест установки HTTP-сервера (-ов)
                
[/config/httpd/servers[]]
server1=/httpd/server1
buildServer=/dev/builds/httpd
intranetSite=/public/prod/apache
    
[/config/http]
port=9080
  

В листинге 3 наши имена серверов выделены жирным шрифтом. Мы сможем обращаться к этому набору пар имя-значение в нашем коде Groovy с помощью глобального контекста Zero. Для простоты используем во всех наших тестах имя сервера server1. Вам нужно изменить значение пути установки server1, чтобы он совпадал с тем, который используется в вашей локальной установке httpd. Другие две записи сервера вам не потребуются – они просто иллюстрируют формат нашей конфигурации.

Прежде чем сохранить и закрыть файл zero.config, нужно добавить такую же запись для /config/http/port, показанного в листинге 3. Это приведет к игнорированию порта Zero по умолчанию 8080, а использоваться будет порт 9080. По умолчанию порт httpd тоже использует порт 8080, а нам не нужно, чтобы наше приложение Zero и процессы httpd конфликтовали.

Теперь надо создать три файла: httpd.groovy, process.groovy и process.bnd, а потом добавить их в каталог проекта /app/resources. Первые два файла – Groovy-скрипты, которые будут представлять два REST-ресурса, описанных в предыдущем разделе. Третий из этих файлов понадобится для конфигурирования REST-шаблонов URI, разрешенных для данных ресурсов.

Начнем с самого легкого, с файла process.bnd. В этот файл нужно добавить всего одну строку, как показано в листинге 4. Эта строка указывает среде исполнения Zero, что она должна разрешить код URI-шаблона /httpd/{id}/process, который мы документировали в своих таблицах REST. Если бы этой строки не было, Zero разрешал бы запросы для /httpd/{id} и /process/{id}, но не /httpd/{id}/process.


Листинг 4. Создание файла BND
                
httpd/process
      

Файл httpd.groovy представляет наши установки httpd и требует двух методов: onList() и onRetrieve(). Первый из них выдает список установок httpd под управлением данного Zero-приложения, а второй – JSON-представление отдельно взятой установки httpd. Сейчас наш интерфейс не поддерживает удалённого изменения установок httpd, поэтому нам не требуются другие REST-методы (onCreate() и т. п.)

В листинге 5 показан код для onList() и onRetrieve(). Реализация onRetrieve() использует для парсинга файлов httpd.conf метод под названием readConfig(); код парсинга для этого метода взят из предыдущего руководства и немного изменён так, что его синтаксис стал больше похож на Groovy. Ознакомиться с реализацией readConfig() можно, загрузив завершённый пример проекта и заглянув в файл /app/resources/httpd.groovy.


Листинг 5. Создание httpd.groovy
                
def onList()
{
    request.view = "JSON";
    request.json.output = config.httpd.servers[0].keySet();
    render();
}

def onRetrieve()
{
    def serverConfig = readConfig();
        
    if (serverConfig.isEmpty())
    {
        request.status = 404;
        return;
    }
        
    request.view = "JSON";
    request.json.output = [
        name: serverConfig.ServerName, 
        port: serverConfig.Listen, 
        threads: serverConfig.ThreadsPerChild, 
        admin: serverConfig.ServerAdmin
    ];
    render();
}
      

Листинг 5 показывает, что мы выбираем те свойства httpd-конфигурации, доступ к которым хотим обеспечить при создании ответной информации JSON. Как и в нашей WS-*версии, мы используем более общие имена для свойств (чтобы интерфейс не был ограничен Apache), а также игнорируем вопросы безопасности. Обратите также внимание на то, что мы используем простой HTTP-код состояния (404), который показывает, что сервер не существует – без дополнительных сообщений, например, о типе ошибки. Метод onRetrieve() концептуально похож на имеющийся в WS-RP GetResourcePropertyDocument, но отсутствует схема для валидации результатов, а "ошибки" определяются на транспортном уровне (HTTP), а не на уровне приложения.

Определив ресурсы нашей установки httpd, мы можем перейти к ресурсам процессов httpd. В листинге 6 показана реализация process.groovy; она очень простая, потому что этот тип ресурса требуется только для запуска сервера и для его остановки, а также для проверки наличия процесса httpd. Слегка отходя от общих принципов REST-проектирования, мы пользуемся HTTP POST для создания процесса httpd (запуск сервера), HTTP DELETE для окончания процесса httpd (остановка сервера) и HTTP GET для определения того, работает ли сервер. Эти три операции соответствуют методам onCreate(), onDeleteCollection() и onList().


Листинг 6. Создание процесса process.groovy
                
def onCreate()
{
    Runtime.getRuntime().exec("${getServerHome()}/bin/httpd -k start");    
    request.status = 201;
}

def onDeleteCollection()
{
    Runtime.getRuntime().exec("${getServerHome()}/bin/httpd -k stop");
    request.status = 204;
}

def onList()
{
    def pidFile = new File(getServerHome(), "logs/httpd.pid");
        
    if (!pidFile.exists())
        request.status = 404;
}
      

Методы onCreate() и onDeleteCollection() используют тот же код, который я использовал для запуска и остановки сервера в предыдущем руководстве для запуска и остановки сервера; конкретнее, они используют API-интерфейс Runtime.exec() из JDK для загрузки исполняемого файла httpd и выполнения соответствующей команды. Это полностью соответствует специальным операциям Start и Stop в нашем интерфейсе WS-*. Метод onList() пользуется тем, что httpd при каждом запуске создает для процесса файл ID или PID и проверяет наличие этого файла, определяя таким образом, работает сервер или нет; если файл PID не существует, этот метод выдает сообщение 404 Not Found вместо обычного 200 OK. Повторю, что можно получить весь файл process.groovy, загрузив пример проекта.

Теперь у нас имеется полная реализация REST API для httpd, но нет простого способа ее тестирования. В следующем разделе мы создадим REST-документацию для своих Groovy-методов и воспользуемся инструментом RESTdoc для тестирования нашего API без написания кода. Фактически вы сможете верифицировать операции, которые ваш код осуществляет в httpd, даже не выходя из браузера.

Предоставление доступа к REST API с помощью RESTdoc

В последнем разделе мы написали много кода, но не документировали его. Что касается скриптов Zero для REST-ресурсов, хорошая документация не только поможет запомнить, как работает ваш код – она поможет программистам на стороне клиента создавать и тестировать собственный клиентский код. Можно написать комментарии типа JavaDoc для своих методов Groovy и PHP, а также с помощью инструмента от Zero RESTdoc создать REST-таблицы и тестовые страницы для них (дополнительную информацию о RESTdoc вы найдете в разделе Ресурсы). Мы будем добавлять комментарии RESTdoc в наши скрипты Groovy, чтобы можно было протестировать наш httpd-интерфейс.

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


Листинг 7. Документирование httpd.groovy
                
/**
 *
 * @success 200 Возвращает список имен серверов,
 * которые можно использовать для создания серверных URI.
 * @format text/json
 * @example
 * [
 *   "server1", 
 *   "server2",
 *   ...
 * ]
 *
 */
def onList()
{
    ...
}

/**
 *
 * @success 200 Возвращает данные конфигурации для данного имени сервера.
 * @error 404 Если отсутствует сервер, связанный с данным именем.
 * @format text/json
 * @example
 * {
 *    "name": "server1.ibm.com", 
 *    "port": 8080, 
 *    "threads": 250, 
 *    "admin": "admin@us.ibm.com"
 * }
 *
 */
def onRetrieve()
{
    ...
}
      

Теперь, когда у нас есть комментарии RESTdoc, можно запускать приложение Zero (введите команду zero run) и перейти по адресу http://localhost:9080/resources/docs в своем браузере. Вы должны увидеть страницу индекса с перечислением всех типов REST-ресурсов в вашем приложении, в том числе httpd и process. Щелкните по ссылке httpd, и вы увидите страницу как на рисунке 1. На этой странице показаны все URI-шаблоны, которые можно использовать для манипулирования данным типом ресурсов, и информация, необходимая для кода, обрабатывающего запросы и ответы.


Рисунок 1. Скриншот страницы RESTdoc
Рисунок 1. Скриншот страницы RESTdoc

Тестирование REST API с помощью RESTdoc

Тестирование наших REST API можно осуществить с помощью тех же страниц, которые их описывают. На странице httpd в RESTdoc нажмите на /httpd, это – первый URI-шаблон, и вы увидите диалог, показанный на рисунке 2. Этот URI-шаблон не содержит переменных, поэтому все, что нужно сделать, – нажать кнопку Send, тогда вы увидите ответ, содержащий список имен серверов – те самые имена, которые вы включили в свой файл zero.config. Этот ответ показан на рисунке 3.


Рисунок 2. Скриншот страницы запроса RESTdoc
Рисунок 2. Скриншот страницы запроса RESTdoc

Рисунок 3. Скриншот страницы ответа RESTdoc
Рисунок 3. Скриншот страницы ответа RESTdoc

Нажмите на второй URI-шаблон /httpd/{httpdId} и введите server1, чтобы задать значение переменной httpdId. Обратите внимание, что этот инструмент не позволяет нажать кнопку Send, пока вы не введете значение и не завершите URI-шаблон. Нажмите на Send, и вы увидите ответ с JSON-представлением вашей установки httpd.

Наш последний тест интерфейса RESTdoc связан с функциями "start" и "stop", которые мы скопировали из предыдущего руководства. В нашем интерфейсе на основе Zero, чтобы запустить или остановить сервер, нужно HTTP POST или HTTP DELETE на /httpd/{httpdId}/process, чтобы запустить или остановить сервер. Проверить функции запуска и останова можно так:

  1. Выберите ресурс process из индекса RESTdoc.
  2. Нажмите на шаблон HTTP POST URI и введите server1, чтобы задать значение переменной httpdId. Нажмите Send.
  3. В другом окне браузера (или на другой вкладке) перейдите по адресу http://localhost:8080. Если вы увидите окно приветствия httpd по умолчанию, все работает!
  4. На странице RESTdoc нажмите шаблон HTTP DELETE URI и введите server1, чтобы задать значение переменной httpdId. Нажмите Send.
  5. Обновите страницу http://localhost:8080. Вы получите сообщение о превышении времени ожидания, потому что сервер уже не работает.
  6. На странице RESTdoc нажмите шаблон HTTP GET URI и введите server1, чтобы задать значение переменной httpdId. Нажмите Send.
  7. Вы увидите код состояния 404, что подтверждает: сервер остановлен, а его артефакты (файл PID) удалены.

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

Заключение

Прочитав эту статью, вы смогли создать REST-интерфейс на основе Zero для httpd, и он так же функционально завершён, как наша WS-*версия на основе Apache Muse. Комбинирование скриптов Groovy и комментариев RESTdoc обеспечивает те же функции и действия, которые мы получали с помощью Java-классов и WSDL. Таким образом, мы показали, что REST может решать задачи, которые традиционно считались слишком сложными для HTTP. Как у REST, так и у WS-* есть свои плюсы и минусы, и ваш выбор будет меняться от проекта к проекту. Мы не пропагандируем ни один из методов, мы просто показали, что каждый из них имеет право на существование.




В начало


Загрузка

ОписаниеИмяРазмерМетод загрузки
Пример приложенияwa-pz-httprest.zip8KBHTTP
Информация о методах загрузки


Ресурсы

Научиться

Получить продукты и технологии
  • Загрузите HTTP-сервер Apache для запуска приложений, которые мы создаём в нашем руководстве (EN).

  • Загрузите Project Zero и начните использовать передовые методики, рассматриваемые в данной статье (EN).


Обсудить


Об авторе

Ден Джемиоло (Dan Jemiolo) работает программистом-консультантом в команде IBM Autonomic Computing в Research Triangle Park, NC. Он возглавлял проектирование и разработку Apache Muse 2.0 и продолжает работать над проектом. Ден также участвовал в WS-RF TC как редактор спецификации по WS-ResourceMetadataDescriptor и в стратегии IBM по расширению применения стандартов Web-сервисов. Он пришел в IBM два года назад, после того как получил степень магистра вычислительных наук в Rensselaer Polytechnic Institute.




Выскажите мнение об этой странице


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



 


 


 


Поделиться этой статьей:

забобрить забобрить memori сохранить в memori




В начало


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