Статьи

Гибридное клиент-серверное программирование: идеальный выбор

Прежде всего мы должны определить, что такое клиент-ориентированное и серверно-ориентированное программирование в сети.

Клиент-ориентированное программирование

Вы делаете клиент-ориентированное программирование, когда создаете код, который будет выполняться на клиенте. Например, любой код JavaScript, созданный вами вручную, ориентирован на клиента (независимо от того, используете ли вы библиотеку JavaScript). Чем больше пользовательских JavaScript, тем больше клиент-ориентированных приложений.

Даже если вы пишете на Java с помощью GWT, эта технология ориентирована на клиента, потому что ваш код будет выполняться на клиенте.

Сервер-ориентированное программирование

Вы делаете серверно-ориентированное программирование, когда ваш код будет выполняться на сервере, конечно, ваш код будет генерировать HTML и может генерировать JavaScript под капотом.

Клиент и серверное несоответствие

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

Несмотря на то, что все изменилось с одностраничным интерфейсом , в таких сайтах / приложениях с интенсивным использованием AJAX и JavaScript веб-платформы, ориентированные на клиента и серверы, как правило, слишком инвазивны с тоннами уже созданных «черных ящиков» компонентов. То есть не так много места для сосуществования и сотрудничества, поэтому вы обычно вынуждены выбирать один подход и одну структуру. Например, вы вряд ли можете добавить собственный код JavaScript, чтобы добавить некоторую ценность, добавленную к серверно-ориентированным компонентам. Верно и обратное: некоторые компоненты на основе JavaScript слишком закрыты, чтобы их можно было расширить с помощью какой-то серверно-ориентированной обработки.

Еще одна проблема — многие уже созданные компоненты JavaScript предназначены только для приложений на основе страниц. Одностраничный интерфейс не очень поддерживается, в этой парадигме фрагменты страницы заменяются по требованию, обычно загружая новые компоненты.

Как согласовать клиент-серверные подходы с ItsNat

Несмотря на свою серверно-ориентированную природу, ItsNat   очень гибок. С некоторой осторожностью вы можете создавать веб-приложения с зонами, контролируемыми ItsNat, и зонами, управляемыми пользовательским кодом JavaScript. Пользовательский код JavaScript может быть возвращен в результате запросов AJAX, а атрибуты любого элемента могут быть изменены с помощью пользовательского кода JavaScript, например, чтобы обеспечить некоторый вид визуального эффекта (обычно это обработчики onX, атрибуты стиля и класса), в этом случае Синхронизация клиент-сервер нарушена (атрибуты изменены только в клиенте), но это не проблема в ItsNat.

Начиная с версии ItsNat v1.1 была представлена ​​новая функция: отсоединение узлов от клиента , серверные DOM-узлы могут быть удалены только в том случае, если серверные оставшиеся клиентские DOM-узлы не повреждены. Позже это же поддерево DOM на сервере может быть снова подключено синхронно с клиентом.

Эта функция очень интересна, чтобы принести новый мир совместной клиент-серверной разработки. В следующем примере показано, как можно полностью управлять типичным компонентом JavaScript в «черном ящике» с сервера в приложении одностраничного интерфейса.

JCarousel — это красивый компонент на основе jQuery, который обеспечивает движение карусели для списка изображений. Онлайн пример .

JCarousel является «полу» компонентом черного ящика, поскольку возможны некоторые шаблоны, список изображений определяется нами примерно так:

      <ul id="carouselId" class="jcarousel-skin-tango">
<li><img src="hybridcs/img/199481236_dc98b5abb3_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481072_b4a0d09597_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481087_33ae73a8de_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481108_4359e6b971_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481143_3c148d9dd3_s.jpg" width="75" height="75" alt="" /></li>
</ul>

В каком-то месте страницы этот скрипт должен быть выполнен, чтобы вызвать JCarousel для рендеринга карусели при загрузке страницы:

jQuery(document).ready(function() {
jQuery('#carouselId').jcarousel();
}

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

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

В следующем примере показано, как мы можем использовать jCarousel как клиентское расширение ItsNat на клиенте, но полностью управляемое на сервере.

Мы собираемся создать веб-приложение на основе ItsNat.

Создайте пустое веб-приложение с предпочитаемой вами IDE, имя контекста не имеет значения, в этом примере мы будем использовать «неопределенность».

Скачайте ItsNat и следуйте инструкциям « Что нужно новому веб-приложению на основе ItsNat? » 

Загрузите jCarousel и распакуйте (v0.2.7 — версия, используемая в этом руководстве).

Создайте общедоступную веб-папку с именем «hybridcs» и скопируйте в нее папку «jsor-jcarousel-XXXXXX» и переименуйте в «jcarousel». Например, файл jquery.jcarousel.min.js должен быть открытым как:

HTTP: // локальный: 8084 / inexperiments / hybridcs / jcarousel / Библиотека / jquery.jcarousel.min.js

Номер порта может быть другим.

Чтобы избежать удаленной загрузки изображений из Flickr (только для производительности), скопируйте изображения, связанные в примерах jCarousel: 

http://static.flickr.com/66/199481236_dc98b5abb3_s.jpg
http://static.flickr.com/75/199481072_b4a0d09597_s.jpg
http://static.flickr.com/57/199481087_33ae73a8de_s.jpg
http: // статический .flickr.com / 77 / 199481108_4359e6b971_s.jpg
http://static.flickr.com/58/199481143_3c148d9dd3_s.jpg
http://static.flickr.com/72/199481203_ad4cdcf109_s.jpg
http://static.flickr.com/ 58 / 199481218_264ce20da0_s.jpg
http://static.flickr.com/69/199481255_fdfe885f87_s.jpg
http://static.flickr.com/60/199480111_87d4cb3e38_s.jpg
http://static.flickr.com/70/229228324_0822jb70fa_

В общую папку «hybridcs / img»

Создайте новый сервлет с именем «expservlet »(это только пример и не является обязательным) с вашей предпочтительной IDE. Исходный код по умолчанию и регистрация в web.xml в порядке.

Заменить следующим кодом:

import inexp.hybridcs.HybridCSLoadListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.itsnat.core.ItsNatServletConfig;
import org.itsnat.core.ItsNatServletContext;
import org.itsnat.core.http.HttpServletWrapper;
import org.itsnat.core.http.ItsNatHttpServlet;
import org.itsnat.core.tmpl.ItsNatDocumentTemplate;

public class inexpservlet extends HttpServletWrapper
{
public void init(ServletConfig config) throws ServletException
{
super.init(config);
ItsNatHttpServlet itsNatServlet = getItsNatHttpServlet();
ItsNatServletConfig itsNatConfig = itsNatServlet.getItsNatServletConfig();
ItsNatServletContext itsNatCtx = itsNatConfig.getItsNatServletContext();
itsNatCtx.setMaxOpenDocumentsBySession(5);
// To limit the memory of bots identified as legitimate browsers and abusive users
String pathPrefix = getServletContext().getRealPath("/") + "/WEB-INF/";
pathPrefix = pathPrefix + "hybridcs/pages/";
ItsNatDocumentTemplate docTemplate;
docTemplate = itsNatServlet.registerItsNatDocumentTemplate("hybridcs","text/html", pathPrefix + "hybridcs.xhtml");
docTemplate.addItsNatServletRequestListener(new HybridCSLoadListener());
}
}

Добавьте следующий чистый HTML-шаблон (который зарегистрирован под именем «hybridcs»):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Hybrid Client-Server Centric Programming</title>

<link href="hybridcs/jcarousel/style.css" rel="stylesheet" type="text/css" />
<!-- jQuery library -->
<script type="text/javascript" src="hybridcs/jcarousel/lib/jquery-1.4.2.min.js"></script>
<!-- jCarousel library -->
<script type="text/javascript" src="hybridcs/jcarousel/lib/jquery.jcarousel.min.js"></script>
<!-- jCarousel skin stylesheet -->
<link rel="stylesheet" type="text/css" href="hybridcs/jcarousel/skins/tango/skin.css" />
</head>
<body xmlns:itsnat="http://itsnat.org/itsnat" >

<h1>Hybrid Client-Server Centric Programming</h1>

<p>This example shows how <a href="http://www.itsnat.org">ItsNat</a> can cooperate with client
JavaScript libraries like <a href="http://jquery.com/" target="_blank">jQuery</a> and black-boxed JS components like <a href="http://sorgalla.com/jcarousel/" target="_blank">jCarousel</a>.
</p>

<br />
<span id="carouselContainerId" itsnat:nocache="true">
<ul id="carouselId" class="jcarousel-skin-tango">
<li><img src="hybridcs/img/199481236_dc98b5abb3_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481072_b4a0d09597_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481087_33ae73a8de_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481108_4359e6b971_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481143_3c148d9dd3_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481203_ad4cdcf109_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481218_264ce20da0_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199481255_fdfe885f87_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/199480111_87d4cb3e38_s.jpg" width="75" height="75" alt="" /></li>
<li><img src="hybridcs/img/229228324_08223b70fa_s.jpg" width="75" height="75" alt="" /></li>
</ul>
</span>

<br/>
<div itsnat:nocache="true">
URL of the new Image: <br/>
<input id="imgURLId" type="text" size="50" /> <br/>
<button id="addImageFirstId">Add to the Begining</button>
<button id="addImageLastId">Add to the End</button>
</div>

</body>
</html>

в файл «WEB-INF / hybridcs / pages / hybridcs.xhtml»

Создайте Java-файл HybridCSLoadListener в пакете uninp.hybridcs:

package inexp.hybridcs;
import org.itsnat.core.ItsNatServletRequest;
import org.itsnat.core.ItsNatServletResponse;
import org.itsnat.core.event.ItsNatServletRequestListener;
import org.itsnat.core.html.ItsNatHTMLDocument;

public class HybridCSLoadListener implements ItsNatServletRequestListener
{
public HybridCSLoadListener() { }
public void processRequest(ItsNatServletRequest request, ItsNatServletResponse response)
{
new HybridCSDocument((ItsNatHTMLDocument)request.getItsNatDocument());
}
}

Метод processRequest будет вызываться при загрузке страницы.

И файл Java HybridCSDocument в том же пакете:

package inexp.hybridcs;
import org.itsnat.comp.ItsNatComponentManager;
import org.itsnat.comp.text.ItsNatHTMLInputText;
import org.itsnat.core.domutil.ElementList;
import org.itsnat.core.domutil.ItsNatTreeWalker;
import org.itsnat.core.html.ItsNatHTMLDocument;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.html.HTMLDocument;
import org.w3c.dom.html.HTMLImageElement;

public class HybridCSDocument implements EventListener
{
protected ItsNatHTMLDocument itsNatDoc;
protected Element carouselContainerElem;
protected Element carouselElem;
protected Element itemPatternElem;
protected ItsNatHTMLInputText imgURLComp;
protected Element addImageFirstElem;
protected Element addImageLastElem;
protected DocumentFragment content;
protected int size;

public HybridCSDocument(ItsNatHTMLDocument itsNatDoc)
{
this.itsNatDoc = itsNatDoc;

HTMLDocument doc = itsNatDoc.getHTMLDocument();
this.carouselContainerElem = doc.getElementById("carouselContainerId");
this.carouselElem = doc.getElementById("carouselId");
this.itemPatternElem = ItsNatTreeWalker.getFirstChildElement(carouselElem);
this.content = (DocumentFragment)itsNatDoc.disconnectChildNodesFromClient(carouselContainerElem);

ItsNatComponentManager compMgr = itsNatDoc.getItsNatComponentManager();
this.imgURLComp = (ItsNatHTMLInputText)compMgr.createItsNatComponentById("imgURLId");
HTMLImageElement firstImgElem = (HTMLImageElement)itemPatternElem.getFirstChild();
imgURLComp.setText(firstImgElem.getSrc());

this.addImageFirstElem = doc.getElementById("addImageFirstId");
((EventTarget)addImageFirstElem).addEventListener("click", this, false);
this.addImageLastElem = doc.getElementById("addImageLastId");
((EventTarget)addImageLastElem).addEventListener("click", this, false);

ElementList list = itsNatDoc.getElementGroupManager().createElementList(carouselElem,false);
this.size = list.getLength();

StringBuilder code = new StringBuilder();
code.append("jQuery(document).ready(function() {");
code.append(" jQuery('#carouselId').jcarousel(); ");
code.append(" }); ");

itsNatDoc.addCodeToSend(code.toString());
}

public void handleEvent(Event evt)
{
EventTarget currTarget = evt.getCurrentTarget();
if (currTarget == addImageFirstElem || currTarget == addImageLastElem)
{
if (size == 30)
{
itsNatDoc.addCodeToSend("alert('Too many images');");
return;
}

size++;

int index;
// Pattern: <li><img src="image url" width="75" height="75" alt="" /></li>
Element newItemElem = (Element)itemPatternElem.cloneNode(true);
HTMLImageElement newImgElem = (HTMLImageElement)newItemElem.getFirstChild();
String newUrl = imgURLComp.getText();
newImgElem.setSrc(newUrl);
if (currTarget == addImageFirstElem)
{
carouselElem.insertBefore(newItemElem, carouselElem.getFirstChild());
index = 1;
}
else
{
carouselElem.appendChild(newItemElem);
index = size;
}
carouselContainerElem.appendChild(content);
this.content = (DocumentFragment)itsNatDoc.disconnectChildNodesFromClient(carouselContainerElem);

itsNatDoc.addCodeToSend("jQuery('#carouselId').jcarousel( { start:" + index + " });");
}
}
}

Как видите, большая часть кода — это Java W3C DOM HTML и W3C DOM Events.

Ключевым методом является ItsNatDocument. disconnectChildNodesFromClient (Node) , этот метод удаляет содержимое элемента только на сервере, клиент остается прежним. Таким образом, jCarousel может сделать карусель без проблем.

Поскольку мы сохранили начальную настройку карусели, мы можем восстановить удаленные узлы, просто вставив содержимое снова в родительский элемент отключенного поддерева DOM. Перед вставкой ItsNat автоматически очищает содержимое в клиенте, в этом случае компонент jCarousel полностью удаляется, теперь к клиенту также добавляются новые узлы, и снова клиент синхронизируется с сервером, затем мы вносим необходимые изменения в исходный. layout (добавить новое изображение в указанную позицию) и jCarousel вызывается снова для рендеринга нового компонента, в этом случае предоставляется новый параметр для прокрутки компонента до нового изображения.

К счастью, jCarousel обнаруживает, когда компонента больше нет на странице, без проблем и может отображать новые компоненты после фазы загрузки страницы.

Разверните, запустите и откройте пример с этим URL (порт может отличаться)

HTTP: // локальный: 8084 / inexperiments / inexpservlet itsnat_doc_name = hybridcs

Посмотрите, что он уже развернут онлайн в действии.

Вывод

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