Custom Constraint

В альфреске в Company Home/XML находится position.xml. Задача у меня следующая: на основе этого XML создавать динамический список, собственный constraint для использования в процессе.

Попробовала создать свой LIST, прочитав эту тему - http://www.ecm-alfresco.ru/viewtopic.php?f=14&t=54

 - Расширила класс ListOfValuesConstraint 

package my.constraints;

public class ListOfPositionsConstraint extends ListOfValuesConstraint implements Serializable {
 
	private static Log logger = LogFactory.getLog(BaseComponentGenerator.class);
 
	private static final long serialVersionUID=1;
 
	private FileFolderService fileFolderService;
	private SearchService searchService;
 
	private String POSITIONS_XML = "XML/positions.xml";
	private List<String> av = new ArrayList<String>();
 
	public void setFileFolderService(FileFolderService fileFolderService) {
		this.fileFolderService = fileFolderService;
	}
 
	public void setSearchService(SearchService searchService) {
		this.searchService = searchService;
	}
 
	private List<String> allowedLabels;
 
	public void setAllowedValues(List allowedValues) {}
 
	public void setCaseSensitive(boolean caseSensitive) {}
 
	public void initialize() {
		super.setCaseSensitive(true);
		this.loadData();
	}
 
	public List getAllowedValues() {
		this.loadData();
		return super.getAllowedValues(); 
	}
 
	public List<String> getAllowedLabels() {
		return this.allowedLabels;
	}
 
	public void setAllowedLabels(List<String> allowedLabels) {
		this.allowedLabels=allowedLabels;
	}
 
	public List<SelectItem> getSelectItemList() {
		List<SelectItem> result = new ArrayList<SelectItem>(this.getAllowedValues().size());
		for(int i=0;i<this.getAllowedValues().size();i++) {
			result.add(new SelectItem((Object)this.getAllowedValues().get(i),this.allowedLabels.get(i)));
		}
		return result;
	}
 
 
	protected NodeRef getNodeRef(NodeRef parent, String path) {
		...
	}

	public List parseXML(NodeRef xml) {
		...		
	}
 
	protected void loadData() {
		StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
		ResultSet rs = searchService.query(storeRef, SearchService.LANGUAGE_XPATH, "/app:company_home");
		NodeRef companyHomeRef = null;
 
		List<String> av = new ArrayList<String>();
		List<String> al=new ArrayList<String>();
 
 
		try	{
			if (rs.length() == 0) logger.error("Cann't find Company Home");
			else companyHomeRef = rs.getNodeRef(0);
		} finally {
			rs.close();
		}
 
		if (companyHomeRef!=null) {
			NodeRef pNode = getNodeRef(companyHomeRef, POSITIONS_XML);
 
			if (pNode!=null) {
				av = parseXML(pNode);
				al = av;
			} else logger.error("Unable to locate "+POSITIONS_XML+" path");
		}
 
		super.setAllowedValues(av);
		this.setAllowedLabels(al);
 
	}
}

 - создала service-context.xml и поместила его в tomcat/shared/classes/alfresco/extension/: 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
 
	<bean id="ListOfPositionsConstraint" 
			class="my.constraints.ListOfPositionsConstraint" >
		<property name="searchService">
			<ref bean="searchService" />
		</property>
		<property name="fileFolderService">
			<ref bean="fileFolderService"/>
		</property>
	</bean>
 
</beans>

 - Добавила в модель следующее: 

...
 
 	<constraints>
		<constraint name="fappgu10:positionsLabel" type="myr.constraints.ListOfPositionsConstraint">
			<parameter name="allowedValues">
				<list>
				</list>
			</parameter>
			<parameter name="caseSensitive">
				<value>true</value>
			</parameter>
		</constraint>
	</constraints>
 
 ...
 
		<type name="fappgu10:performRequest">
			<parent>bpm:workflowTask</parent>
			<properties>
				<property name="fappgu10:positions">
					<type>d:text</type>
					<constraints>
						<constraint ref="fappgu10:positionsLabel" />
					</constraints>
				</property>
			</properties>
			<overrides>
				<property name="bpm:packageActionGroup">
					<default>add_package_item_actions</default>
				</property>
				<property name="bpm:packageItemActionGroup">
					<default>edit_package_item_actions</default>
				</property>
			</overrides>
			<mandatory-aspects>
				<aspect>fappgu10:mail</aspect>
				<aspect>fappgu10:mailText</aspect>
				<aspect>fappgu10:sendByEmail</aspect>
				<aspect>fappgu10:sendByMail</aspect>
				<aspect>fappgu10:phone</aspect>
				<aspect>fappgu10:fname</aspect>
				<aspect>fappgu10:lname</aspect>
				<aspect>fappgu10:pname</aspect>
				<aspect>fappgu10:address</aspect>
			</mandatory-aspects>
		</type>
 
...

 

 Альфреска падает при рестарте с сообщением о неудачной попытке задеплоить модель.

Что я делаю не так?

UPD: Заработало. Новый код:

 

package my.constraints;
 
public class ListOfPositionsConstraint extends ListOfValuesConstraint implements Serializable {
 
	org.alfresco.repo.jscript.ScriptLogger logger = new org.alfresco.repo.jscript.ScriptLogger();
 
	private static final long serialVersionUID=1;
 
 
	private String POSITIONS_XML = "XSL/positions.xml";
	private List<String> av = new ArrayList<String>();
 
	private static ServiceRegistry registry;
	private static Repository repository;
 
	public ServiceRegistry getServiceRegistry()	{
		return registry;
	}
 
	public void setServiceRegistry(ServiceRegistry registry)	{
		ListOfPositionsConstraint.registry = registry;
	}
 
	public void setRepository(Repository repository) {
		ListOfPositionsConstraint.repository = repository;
	}
 
	public Repository getRepository()	{
		return repository;
	}
 
	private List<String> allowedLabels;
 
	public void setAllowedValues(List allowedValues) {}
 
	public void setCaseSensitive(boolean caseSensitive) {}
 
	public void initialize() {
		super.setCaseSensitive(true);
		this.loadData();
	}
 
	public List getAllowedValues() {
		this.loadData();
		return super.getAllowedValues(); 
	}
 
	public List<String> getAllowedLabels() {
		return this.allowedLabels;
	}
 
	public void setAllowedLabels(List<String> allowedLabels) {
		this.allowedLabels=allowedLabels;
	}
 
	public List<SelectItem> getSelectItemList() {
		List<SelectItem> result = new ArrayList<SelectItem>(this.getAllowedValues().size());
		for(int i=0;i<this.getAllowedValues().size();i++) {
			result.add(new SelectItem((Object)this.getAllowedValues().get(i),this.allowedLabels.get(i)));
		}
		return result;
	}
 
	protected NodeRef getNodeRef(NodeRef parent, String path) {
...
	}
 
	public List parseXML(NodeRef xml) {
 ...
	}
 
	protected void loadData() {
		NodeRef companyHomeRef = null;
		try {
			companyHomeRef = getRepository().getCompanyHome();
		} catch(Exception ex){
			logger.error(ex.toString());
		}
		List<String> av = new ArrayList<String>();
		List<String> al=new ArrayList<String>();
		if (companyHomeRef!=null) {
			NodeRef pNode = getNodeRef(companyHomeRef, POSITIONS_XML);
 
			if (pNode!=null) {
				av = parseXML(pNode);
				al = av;
			} else logger.error("Unable to locate "+POSITIONS_XML+" path");
		}
 
		super.setAllowedValues(av);
		this.setAllowedLabels(al);
 
	}
}

 

service-context.xml:

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
 
	<bean id="ListOfPositionsConstraint" 
			class="my.constraints.ListOfPositionsConstraint" >
 
		<property name="serviceRegistry">
			<ref bean="ServiceRegistry" />
		</property>
		<property name="repository">
			<ref bean="repositoryHelper" />
		</property>
	</bean>
 
</beans>

 

В модели изменений не было.

 

Комментарии

fufler аватар

А можно увидеть то самое сообщение?

Angelina аватар

Ошибка упирается в

Caused by: java.lang.NullPointerException
	at ru.cos.orchestra.fapmc.constraints.ListOfPositionsConstraint.loadData(ListOfPositionsConstraint.java:152)
	at ru.cos.orchestra.fapmc.constraints.ListOfPositionsConstraint.initialize(ListOfPositionsConstraint.java:54)
	at org.alfresco.repo.dictionary.M2ConstraintDefinition.resolveInternal(M2ConstraintDefinition.java:287)
	at org.alfresco.repo.dictionary.M2ConstraintDefinition.resolveDependencies(M2ConstraintDefinition.java:119)
	at org.alfresco.repo.dictionary.CompiledModel.<init>(CompiledModel.java:102)
	... 70 more

 Добавила в метод loadData форсированное заполнение списков значений и меток:

 

al.add("position");
av.add("position");
super.setAllowedValues(av);
this.setAllowedLabels(al);

 

Та же ошибка...

 

Angelina аватар

Поняла, где падает. Падает уже на

StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
		ResultSet rs = searchService.query(storeRef, SearchService.LANGUAGE_XPATH, "/app:company_home");
		NodeRef companyHomeRef = null;

 Убрала из метода loadData все, кроме форсированной инициализации списков. Так работает. Остается понять, почему падает  на этих строках.

Angelina аватар

Поняла, что нулевым получается ResultSet. Попробовала по-другому запросить company home:

searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, "PATH:\""+"/app:company_home"+"/*\"")

и опять получается нул.

Может, что-то надо включить в альфреске?

Angelina аватар

Вообщем, проблемма в том, что я не могу никак вытащить компани хоум.  NullPointerExceptoin

Инъектировала в bean ServiceRegistry и repositoryHelper, попыталась вытащить через companyHomeRef = this.repository.getCompanyHome(). То же самое - пусто. Попробую через CMIS, но как-то странно... почему я не могу вытащить альфрескиными апи.

fufler аватар

Странно, должно бы работать. У меня с ходу нет идей, что может быть не так.

Angelina аватар

Проблему наконец решила. Пошла путем товарища Джина Бармаша. С одной поправкой: не использовала searchService, поскольку его использование привело к ошибку с правами доступа (замечу, что все делается из-под админа). Сейчас напишу апдейт к посту, вдруг кому полезно будет.

fufler аватар

Предлагаю открыть тикет на JIRA, потому что всё выглядит как-то странно.

Angelina аватар

Смотрите, ошибка с нулом исправилась после того, как я изменила сеттеры сервисных параметров. Но при использовании searchService возникла ошибка  ProviderNotFoundException (я ее назвала ошибкой доступа) А на это дело я нашла следующий  этот талончик. Он вроде у них закрыт за невозможностью воспроизвести... Может быть, все же я что-то не то делаю...

 

fufler аватар

На сеттеры я внимания не обратил :) Я обычно использую такое описание бина

<bean id="beanId" class="className">
	<property name="repository" ref="repositoryHelper" />
	<property name="serviceRegistry" ref="ServiceRegistry" />
</bean>

 и такие сеттеры

public void setRepository(Repository repository) {
	this.repository = repository;
}
 
public void setServiceRegistry(ServiceRegistry serviceRegistry) {
	this.serviceRegistry = serviceRegistry;
}

 После этого можно использовать

if (serviceRegistry != null)
	if (serviceRegistry.isServiceProvided(ServiceRegistry.SEARCH_SERVICE)
		serviceRegistry.getSearchService().query(...)
Angelina аватар

Вот с такими сеттерами и бинами у меня нулы и получались в собственном списке. Хотя, к примеру, в модулях истории процессов я также, как  и у Вас, использую сеттеры :)

Вообщем, странная история...

fufler аватар

Что-то я торможу. Оно ведь и не должно работать :) В модели указан класс, инстанс которого используется как constraint.  При таком подходе не происходит dependency injection, соответственно, получаем null'ы.

 Уважаемая Angelina.
Я только недавно начал работать с Alfresco, поэтому у меня вызывает затруднение даже такой простой вопрос, как куда класть файлы java. Не могли бы вы проконсультировать меня, а еще лучше дать рабочие файлы этого примера с путями, что-бы я мог посмотреть их в реальной работе.
Angelina аватар
К сожалению, работающий пример дать не могу. Но что куда класть - написано прямо в тексте темы. И кладете Вы не java коды, а скомпилированные файлы.

В документации альфреско есть готовые примеры. Вот здесь, например, описано, как делать вэб скрипты, используя джаву.
Здесь есть как сделать выпадающий список красивым.
http://forums.alfresco.com/forum/developer-discussions/repository-servic...

Здесь есть объяснение пустого SerchService
https://forums.alfresco.com/forum/developer-discussions/alfresco-api/dyn...

"Sine you implements a dynamic list of constraints like this ,then some one can change your dynamic list of constraints by adding/modifying/deleting node. Adding nodes will not be a problem, but deleting nodes will leave nodes with this property value in an invalid state.

I think the reason why it gets locked indefinetly in the point of the query is because during alfresco startup the search component is busy doing some initialization work ( for example checks consistence between the metadata, indexes and content) and not ready to provide search searvice.

You can try to define your bean like this

	<bean id="mytests.myconstraints.ConstraintInitializer" class="org.myproject.mycontraints.LoadingMyCombo" depends-on="repositoryEndBootstrapBean"> 

Or you can just deploy your custom model into Company Home/Dictionary/Models in Alfresco repository instead of deploying content model XML file in classpath"

Angelina аватар
Все было проще: проблема решилась, когда сеттеры и геттеры были правильно написаны. Там наверху fufler хорошо объяснил ошибку. Это мне тогда и помогло.

Впрочем, как показала практика, пользоваться data list-ом гораздо быстрее, чем писать собственный констраинт