В Челябинске опять прекрасная погода, а как там на Брайтон-Бич я не знаю. Суровый челябинский программист снова с вами и мы продолжаем знакомиться с OSGi. В предыдущей заметке мы остановились на вопросе: как сделать так, чтобы наш клиент мог получить информацию от двух сервисов с одинаковыми именами?
Еще раз вернемся к условию нашей задачи. Нам необходимо разработать меню цветов, котороее формировалось бы из палитр, предоставляемых разными бандлами. Мы выбрали такую схему решения: бандлы выставляют ColorizerService'ы, предоставляющие палитры меню. И есть некий бандл, который мы будем называть "центральным", который получает эти палитры от сервисов и объединяет их.
Приступим к реализации? Сначала создадим бандлы с сервисами. Пусть у нас будет два бандла: org.beq.equinox.p1 и org.beq.equinox.p2. Код и манифесты у них будут одинаковыми, отличаться будут лишь массивы цветов и конечно же значения полей Bundle-Name и Bundle-SymbolicName в манифестах. Поэтому сосредоточимся только на одном бандле org.beq.equinox.p1.
Код бандла будет состоять из класса-сервиса ColorizerService и класса-активатора Activator. Код сервиса следующий:
- package org.beq.equinox.p1;
- import org.beq.equinox.colorizer.IColorizer;
- public class ColorizerService implements IColorizer
- {
- {
- "Red", "Green", "Blue"
- };
- }
- }
Собственно код тривиален. Скажу лишь, что интерфейс IColorizer предоставляется нам "центральным" бандлом.
Активатор бандла org.beq.equinox.p1:
- package org.beq.equinox.p1;
- import org.beq.equinox.colorizer.IColorizer;
- import org.osgi.framework.BundleActivator;
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.ServiceRegistration;
- private ServiceRegistration registration;
- /*
- * (non-Javadoc)
- * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
- */
- // create a service
- IColorizer colorizer = new ColorizerService();
- // registrating the ColorizerService
- registration = context.registerService(IColorizer.class.getName(),
- colorizer, null);
- }
- /*
- * (non-Javadoc)
- * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
- */
- registration.unregister();
- }
- }
Комментировать здесь нечего. Реализован стандартный сценарий регистрации сервиса.
Думаю разумно будет так-же привести манифест бандла:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: P1
Bundle-SymbolicName: org.beq.equinox.p1
Bundle-Version: 1.0.0
Bundle-Activator: org.beq.equinox.p1.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.beq.equinox.colorizer,
org.osgi.framework
Bundle-ManifestVersion: 2
Bundle-Name: P1
Bundle-SymbolicName: org.beq.equinox.p1
Bundle-Version: 1.0.0
Bundle-Activator: org.beq.equinox.p1.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.beq.equinox.colorizer,
org.osgi.framework
Самое интересное - "центральный" бандл org.beq.equinox.colormenu, который будет объединять данные, предоставляемые сервисами ColorizerService. Основным классом бандла является класс ColorMenu, который реализует интерфейс IColorMenu. Основная идея - в классе определена коллекция для хранения палитр и методы добавления палитры в коллекцию и удаления палитры из коллекции.
IColorMenu:
package org.beq.equinox.colormenu;
import java.util.List;
public interface IColorMenu
{
public List<String> getAllItems();
}
import java.util.List;
public interface IColorMenu
{
public List<String> getAllItems();
}
ColorMenu:
package org.beq.equinox.colormenu;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.beq.equinox.colorizer.IColorizer;
public class ColorMenu implements IColorMenu
{
private Collection<IColorizer> colorizers
= Collections.synchronizedCollection(new ArrayList<IColorizer>());
@Override
public List<String> getAllItems()
{
List<String> results = new ArrayList<String>();
for (IColorizer colorizer : colorizers)
{
for (String color: colorizer.getColors())
if (!results.contains(color))
results.add(color);
}
return results;
}
protected synchronized void bindColorizer(IColorizer colorizer)
{
colorizers.add(colorizer);
System.out.println("- bind new colorizer");
}
protected synchronized void unbindColorizer(IColorizer colorizer)
{
colorizers.remove(colorizer);
System.out.println("- unbind colorizer");
}
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.beq.equinox.colorizer.IColorizer;
public class ColorMenu implements IColorMenu
{
private Collection<IColorizer> colorizers
= Collections.synchronizedCollection(new ArrayList<IColorizer>());
@Override
public List<String> getAllItems()
{
List<String> results = new ArrayList<String>();
for (IColorizer colorizer : colorizers)
{
for (String color: colorizer.getColors())
if (!results.contains(color))
results.add(color);
}
return results;
}
protected synchronized void bindColorizer(IColorizer colorizer)
{
colorizers.add(colorizer);
System.out.println("- bind new colorizer");
}
protected synchronized void unbindColorizer(IColorizer colorizer)
{
colorizers.remove(colorizer);
System.out.println("- unbind colorizer");
}
}
Самый главный вопрос - а кто будет добавлять палитры в меню? Ведь перед тем, как добавить палитры в меню их необходимо получить от сервисов. В предыдущей заметке мы рассматривали работу с сервисами и выяснили, что существует класс ServiceTracker, который отслеживает регистрацию сервиса с заданным именем в реестре сервисов OSGi и позволяет получить доступ к зарегистрированному экземпляру сервиса. Получается, чтобы получить доступ к данным, предоставляемым всеми зарегистрированными в реестре сервисам необходимо повесить свой хук на процедуру регистрации сервиса. Роль этого хука будет играть класс ColorizerTracker, являющийся наследником ServiceTracker:
package org.beq.equinox.colormenu;
import org.beq.equinox.colorizer.IColorizer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
public class ColorizerTracker extends ServiceTracker implements ServiceTrackerCustomizer
{
private final ColorMenu menu = new ColorMenu();
public ColorMenu getMenu()
{
return menu;
}
public ColorizerTracker(BundleContext context)
{
super(context, IColorizer.class.getName(), null);
open();
}
@Override
public Object addingService(ServiceReference reference) {
IColorizer colorizer = (IColorizer) context.getService(reference);
menu.bindColorizer(colorizer);
return colorizer;
}
@Override
public void removedService(ServiceReference reference, Object service)
{
menu.unbindColorizer((IColorizer) service);
super.removedService(reference, service);
}
}
import org.beq.equinox.colorizer.IColorizer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
public class ColorizerTracker extends ServiceTracker implements ServiceTrackerCustomizer
{
private final ColorMenu menu = new ColorMenu();
public ColorMenu getMenu()
{
return menu;
}
public ColorizerTracker(BundleContext context)
{
super(context, IColorizer.class.getName(), null);
open();
}
@Override
public Object addingService(ServiceReference reference) {
IColorizer colorizer = (IColorizer) context.getService(reference);
menu.bindColorizer(colorizer);
return colorizer;
}
@Override
public void removedService(ServiceReference reference, Object service)
{
menu.unbindColorizer((IColorizer) service);
super.removedService(reference, service);
}
}
Интерфейс ServiceTrackerCustomizer определяет методы, отслеживающие изменение состояний сервисов. Мы реализовали 2 метода: public Object addingService(ServiceReference reference) и public void removedService(ServiceReference reference, Object service). Соответственно, при регистрации сервиса в реестре мы получим предоставляемую им палитру и сохраним ее в коллекции colorizers.
Интерфейс IColorizer, используемый в этом и сервисных бандлах:
В активаторе достаточно создать объект класса ColorizerTracker для того, чтобы бандл начал реагировать на регистрацию сервисов в реестре. Код активатора:
package org.beq.equinox.colormenu;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private ColorizerTracker tracker;
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
tracker = new ColorizerTracker(context);
printColors();
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
printColors();
tracker.close();
}
private void printColors()
{
IColorMenu menu = tracker.getMenu();
for (String item : menu.getAllItems())
System.out.println(item);
}
}
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private ColorizerTracker tracker;
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
tracker = new ColorizerTracker(context);
printColors();
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
printColors();
tracker.close();
}
private void printColors()
{
IColorMenu menu = tracker.getMenu();
for (String item : menu.getAllItems())
System.out.println(item);
}
}
Также стоит рассмотреть манифест бандла:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: ColorMenu
Bundle-SymbolicName: org.beq.equinox.colormenu
Bundle-Version: 1.0.0
Bundle-Activator: org.beq.equinox.colormenu.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework,
org.osgi.util.tracker
Export-Package: org.beq.equinox.colorizer
Bundle-ManifestVersion: 2
Bundle-Name: ColorMenu
Bundle-SymbolicName: org.beq.equinox.colormenu
Bundle-Version: 1.0.0
Bundle-Activator: org.beq.equinox.colormenu.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework,
org.osgi.util.tracker
Export-Package: org.beq.equinox.colorizer
Здесь следует обратить внимание на то, что мы экспортируем пэкедж org.beq.equinox.colorizer, в котором находится интерфейс IColorizer.
Темерь можно посмотреть на то, как все это работает. Стартуем Equinox и инсталлируем наши бандлы на шину. Порядок установки и id бандлов приведены на рисунке.
Теперь стартуем бандлы в правильном порядке. Правильным в данном случае является такой порядок: сначала сервисы, потом клиент. Как видим клиент успешно получил данные от обоих сервисов и вывел их на консоль:
Теперь остановим все бандлы и произведем их активацию в противоположном порядке: сначала клиент потом сервис. Естественно, что при старте клиента ничего на консоль выведено не будет - потому что сервисы, предоставляющие данные в системе не зарегистрированы.
Единственным интерфейсом к бандлу для нас пока является консоль. Остановим клиентский бандл. Так как в методе stop мы сначала выводим на экран известные бандлу цвета, то при попытке остановить бандл увидим следующее:
Т.е. после того, как мы зарегистрировали сервисы в реестре их данные стали доступны клиентскому бандлу.
Теперь поговорим вот о чем. Идея связки "один клиент - множество сервисов" хороша, но можно ведь реализовать и связку "множество клиентов - один сервис". Давайте рассмотрим ее подробнее.
Применительно к нашей задаче суть идеи следующая: мы регистрируем сервис ColorMenuService в реестре сервисов. Другие бандлы не будут получать от него данные, а наоборот - будут предоставлять их ему. Ключевым звеном в данной связке будет являться MenuTracker, через который осуществляется биндинг данных в сервис.
Собственно код будет основан на коде предыдущего примера. Мы создадим бандл org.beq.equinox.colormenu2, в котором определим сервис и бандлы org.beq.equinox.p1c и org.beq.equinox.p2c, которые будут предоставлять данные нашему сервису.
Начнем с бандла org.beq.equinox.colormenu2. Код сервиса ColorMenuService будет полностью повторять код класса ColorMenu. Экспортируемый бандлом IColorizer тоже остается без изменений. А вот вместо ColorizerTracker мы создадим класс MenuTracker, код которого будет вот таким:
package org.beq.equinox.colormenu;
import java.util.ArrayList;
import java.util.List;
import org.beq.equinox.colorizer.IColorizer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
public class MenuTracker extends ServiceTracker implements ServiceTrackerCustomizer
{
private List<IColorizer> colorizers = new ArrayList<IColorizer>();
private List<IColorizer> colorizers2connect = new ArrayList<IColorizer>();
public MenuTracker(BundleContext context)
{
super(context, ColorMenuService.class.getName(), null);
open();
connectColorizers((ColorMenuService) getService());
}
public MenuTracker(BundleContext context, List<IColorizer> colorizers)
{
super(context, ColorMenuService.class.getName(), null);
if (colorizers != null)
colorizers2connect.addAll(colorizers);
open();
connectColorizers((ColorMenuService) getService());
}
public MenuTracker(BundleContext context, IColorizer colorizer)
{
super(context, ColorMenuService.class.getName(), null);
if (colorizers != null)
colorizers2connect.add(colorizer);
open();
connectColorizers((ColorMenuService) getService());
}
@Override
public Object addingService(ServiceReference reference) {
Object service = super.addingService(reference);
connectColorizers((ColorMenuService) service);
return service;
}
@Override
public void removedService(ServiceReference reference, Object service)
{
colorizers2connect.addAll(colorizers);
colorizers.clear();
super.removedService(reference, service);
}
protected void connectColorizers(ColorMenuService service)
{
if (service != null)
{
for (IColorizer colorizer: colorizers2connect)
connectColorizer(colorizer, service);
colorizers2connect.clear();
}
}
public void connectColorizer(IColorizer colorizer, ColorMenuService service)
{
if (colorizer != null && service != null)
{
service.bindColorizer(colorizer);
colorizers.add(colorizer);
}
}
protected void disconnectColorizers(ColorMenuService service)
{
if (service != null)
{
for (IColorizer colorizer: colorizers)
disconnectColorizer(colorizer, service);
colorizers.clear();
}
}
public void disconnectColorizer(IColorizer colorizer, ColorMenuService service)
{
if (colorizer != null && service != null)
service.unbindColorizer(colorizer);
}
@Override
public synchronized void close()
{
disconnectColorizers((ColorMenuService) getService());
super.close();
}
}
import java.util.ArrayList;
import java.util.List;
import org.beq.equinox.colorizer.IColorizer;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
public class MenuTracker extends ServiceTracker implements ServiceTrackerCustomizer
{
private List<IColorizer> colorizers = new ArrayList<IColorizer>();
private List<IColorizer> colorizers2connect = new ArrayList<IColorizer>();
public MenuTracker(BundleContext context)
{
super(context, ColorMenuService.class.getName(), null);
open();
connectColorizers((ColorMenuService) getService());
}
public MenuTracker(BundleContext context, List<IColorizer> colorizers)
{
super(context, ColorMenuService.class.getName(), null);
if (colorizers != null)
colorizers2connect.addAll(colorizers);
open();
connectColorizers((ColorMenuService) getService());
}
public MenuTracker(BundleContext context, IColorizer colorizer)
{
super(context, ColorMenuService.class.getName(), null);
if (colorizers != null)
colorizers2connect.add(colorizer);
open();
connectColorizers((ColorMenuService) getService());
}
@Override
public Object addingService(ServiceReference reference) {
Object service = super.addingService(reference);
connectColorizers((ColorMenuService) service);
return service;
}
@Override
public void removedService(ServiceReference reference, Object service)
{
colorizers2connect.addAll(colorizers);
colorizers.clear();
super.removedService(reference, service);
}
protected void connectColorizers(ColorMenuService service)
{
if (service != null)
{
for (IColorizer colorizer: colorizers2connect)
connectColorizer(colorizer, service);
colorizers2connect.clear();
}
}
public void connectColorizer(IColorizer colorizer, ColorMenuService service)
{
if (colorizer != null && service != null)
{
service.bindColorizer(colorizer);
colorizers.add(colorizer);
}
}
protected void disconnectColorizers(ColorMenuService service)
{
if (service != null)
{
for (IColorizer colorizer: colorizers)
disconnectColorizer(colorizer, service);
colorizers.clear();
}
}
public void disconnectColorizer(IColorizer colorizer, ColorMenuService service)
{
if (colorizer != null && service != null)
service.unbindColorizer(colorizer);
}
@Override
public synchronized void close()
{
disconnectColorizers((ColorMenuService) getService());
super.close();
}
}
Как видим - класс стал несколько сложнее. У нас теперь 2 коллекции - в одну заносятся переданные в сервис палитры, в другую - доступные для передачи. Основная идея в том, что мы создаем треккер, заполняем коллекцию доступных для передачи в сервис объектов, а как только нужный нам сервис зарегистрируется в системе - мы сразу же инъектируем в него данные. Соответственное, если сервис извлекают из реестра данные становятся не "переданными", а "готовыми к передаче" в сервис. Т.е. как только сервис вновь зарегистрируется в реестре - мы с радостью инъектируем в него данные заново.
Так же мы переопределили метод close. Перед тем, как закрыть треккер необходимо отбиндить данные, переданные в сервис.
Активатор банд;;ла тривиальный. Мы создаем экземпляр класса ColorMenuService и регистрируем его в реестре сервисов.
Манифест бандла следующий:
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: ColorMenu
Bundle-SymbolicName: org.beq.equinox.colormenu2
Bundle-Version: 2.0.0
Bundle-Activator: org.beq.equinox.colormenu.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework,
org.osgi.util.tracker
Export-Package: org.beq.equinox.colorizer, org.beq.equinox.colormenu
Bundle-ManifestVersion: 2
Bundle-Name: ColorMenu
Bundle-SymbolicName: org.beq.equinox.colormenu2
Bundle-Version: 2.0.0
Bundle-Activator: org.beq.equinox.colormenu.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Import-Package: org.osgi.framework,
org.osgi.util.tracker
Export-Package: org.beq.equinox.colorizer, org.beq.equinox.colormenu
Здесь стоит обратить внимание на то, что мы экспортируем 2 пэкеджа: org.beq.equinox.colorizer, содержащий интерфейс IColorizer и org.beq.equinox.colormenu, содержащий наш MenuTracker.
Бандлы org.beq.equinox.p1c и org.beq.equinox.p2c так же не сильно отличаются от org.beq.equinox.p1 и org.beq.equinox.p2 соответственно. Класcы ColorizerService переименованы в Colorizer, а активаторы приняли следующий вид:
package org.beq.equinox.p1c;
import org.beq.equinox.colormenu.MenuTracker;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private MenuTracker tracker;
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
tracker = new MenuTracker(context, new Colorizer());
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
tracker.close();
}
}
import org.beq.equinox.colormenu.MenuTracker;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
private MenuTracker tracker;
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
tracker = new MenuTracker(context, new Colorizer());
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
tracker.close();
}
}
Т.е. подключение к сервису выливается в создание экземпляра класса MenuTracker, которому передается нужный экземпляр класса Colorizer.
Демок приводить не буду, единственное следует учитывать, что правильный порядок активации бандлов поменялся.
Считаю, что гораздо важнее обсудить эти 2 стратегии. Мое мнение таково: вторая стратегия (т.е. стратегия "1 сервис <-> много клиентов) предпочтительнее. Выгод от ее использования я вижу несколько.
Во-первых - концептуальная. Мы можем рассматривать сервис как точку расширения приложения, а треккеры - как механизм использования этой точки расширения. Мы расширяем возможности нашего приложения, передавая в точку расширения некоторую информацию. В данном случае - Colorizer'ы.
Во-вторых - техническая. У нас есть регулярная структура - треккер. Т.е. это значит, что мы можем создать некий базовый класс, использующий дженерики, который будет осуществлять связь с сервисом и биндинг данных в сервис. Конкретные же реализации будут расширять этот стандартный механизм, добавляя в него типичную для себя бизнес-логику.
Именно такой подход реализован в Naumen Kernel, где используется следующая терминология:
- Ресурс - POJO, которое инкапсулирует в себя атомарную порцию данных, передаваемых сервису.
- Сервис - OSGi-сервис, любой ява-класс, который реализует соответствующий интерфейс и позволяет подключать к себе ресурсы.
- Коннектор - класс, наследующий ServiceTracker. Соответственно бандл, желающий расширить сервис должен создать соответствующий коннектор в своем активаторе.
Ну вот пожалуй и все, что я хотел рассказать об использовании сервисов. Однако неясным остался один вопрос - как управлять жизненным циклом бандлов, регистрирующих и использующих сервисы? Как было видно из примеров - порядок старта бандлов имеет важное значение для обеспечения нормального функционирования системы.
Однако есть в OSGi способ облегчить задачу управления жизненным циклом бандлов. Имя ему - декларативные сервисы. О том, что это такое мы и поговорим в следующий раз.
Оставайтесь на связи!
Понравилось сообщение - подпишись на блог
10 комментариев:
На Брайтоне тоже ничего, весна!!
И это не может не радовать )))
Хотелось бы поинтересоваться еще на счет одной интересной для меня темы, а именно Rich Ajax Platform (RAP).. Планируете ли Вы рассказать что нибудь про это?. Материала по данной теме крайне мало, а тем более на русском языке((
Сам я RAP не использовал и в ближайшее время не планирую, поэтому врятли смогу рассказать что-либо интересное.
Эх, жаль.. Так с трудом дается(
Будем ждать новых интересных статей)
насчет: "Как было видно из примеров - порядок старта бандлов имеет важное значение для обеспечения нормального функционирования системы."
Там еще вроде бы есть сервис листенеры. Которые позволяют зарегаться и ждать, когда же сервис появится. Это я видел в вебинаре, в одном из перечисленных в первой статье.
Есть и такое, но суть в том, что кто-то должен стартовать бандл, в котором регистрируется сервис. В этом то и есть вся загвоздка.
Добрый день.
Увидел, что вы по долгу службы используете (или разрабатываете naumen kernel). Крайне любопытно узнать, но заверениям производителя это open-source ядро, однако по известным ссылка, выйти на cvs не получилось (возможно плохо искал), а по той ссылке, что приводите вы, лежит архив с версией двух летней давности.
В связи с эти вопрос, ядро загнулось или просто изменился курс с open-source на другой, или ядро как бы не изменяется, а дописываются только уже закрытые плагины?
Вобщем любопытно было бы знать, как обстоят с ним дела, можно ли его поиспользовать для создания реальных проектов, и где взять свежие исходники?
Спасибо.
Николай.
Здравствуйте.
По поводу Naumen Kernel вопрос сложный, политику его лицензирования по-моему не знает никто. Ядро развивается, но новые сборки не выкладываются в публичный доступ. К SVN также доступ ограничен только офисами компании + пароли/логины разработчиков. Получается некий отход от опенсорц, впрочем компания имеет на это право - весь код принадлежит ей. Однако, то ядро, что выложено в публичный доступ вы имеете право использовать. Другое дело есть ли смысл ведь получать апдейты и исправления ошибок вы не сможете.
Отправить комментарий
Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!