Project

General

Profile

1
package eu.dnetlib.enabling.is.sn.resourcestate;
2

    
3
import java.util.Arrays;
4
import java.util.Collection;
5
import java.util.HashSet;
6
import java.util.Set;
7

    
8
import javax.xml.transform.dom.DOMResult;
9
import javax.xml.ws.wsaddressing.W3CEndpointReference;
10
import javax.xml.xpath.XPathExpressionException;
11
import javax.xml.xpath.XPathFactory;
12

    
13
import org.apache.oro.text.perl.Perl5Util;
14
import org.springframework.beans.factory.annotation.Autowired;
15
import org.springframework.beans.factory.annotation.Required;
16

    
17
import eu.dnetlib.enabling.is.sn.AbstractSubscriptionRegistry;
18
import eu.dnetlib.enabling.is.sn.SubscriptionRegistry;
19
import eu.dnetlib.enabling.is.sn.SubscriptionRequest;
20
import eu.dnetlib.enabling.is.sn.TopicExpressionMatchResult;
21
import eu.dnetlib.enabling.is.sn.rmi.SubscriptionRequestRejectedException;
22

    
23
/**
24
 * Manage subscription for UPDATE/CREATE/DELETE resource-related topic prefixes.
25
 *
26
 * @author marko
27
 *
28
 */
29
public class ResourceStateSubscriptionRegistry extends AbstractSubscriptionRegistry implements SubscriptionRegistry {
30

    
31
	/**
32
	 * subscription DAO.
33
	 */
34
	private ResourceStateSubscriptionDAO subscriptionDao;
35

    
36
	/**
37
	 * subscription request filter.
38
	 */
39
	@Autowired
40
	private SubscriptionRequestFilter subscriptionRequestFilter;
41

    
42
	/**
43
	 * {@inheritDoc}
44
	 *
45
	 * @see eu.dnetlib.enabling.is.sn.SubscriptionRegistry#registerSubscription(eu.dnetlib.enabling.is.sn.SubscriptionRequest)
46
	 */
47
	@Override
48
	public String registerSubscription(final SubscriptionRequest subscription) throws SubscriptionRequestRejectedException {
49
		final TopicExpressionMatchResult prefixMatch = matchPrefix(subscription);
50
		if (prefixMatch == null)
51
			return null;
52

    
53
		final TopicExpressionMatchResult typeMatch = matchType(prefixMatch.getRest());
54
		if (typeMatch == null)
55
			return null; // TODO: decide whether to fail or not
56

    
57
		final TopicExpressionMatchResult idMatch = matchId(typeMatch.getRest());
58

    
59
		if (idMatch == null)
60
			return null; // TODO: decide whether to fail or not
61

    
62
		final ResourceStateSubscription rss = new ResourceStateSubscription(subscription, prefixMatch.getPrefix(), typeMatch.getPrefix(), idMatch.getPrefix(),
63
				idMatch.getRest());
64

    
65
		if (!getSubscriptionRequestFilter().accept(rss))
66
			throw new SubscriptionRequestRejectedException(String.format("rejected subscription request, resourceId: '%s', xpath: '%s', from: %s",
67
					rss.getResourceId(), rss.getXpath(), rss.getSubscriber()));
68

    
69
		return registerSubscription(rss);
70
	}
71

    
72
	/**
73
	 * this registers the real subscription.
74
	 *
75
	 * TODO: am I sure that the overload is a good thing here?
76
	 *
77
	 * @param subscription
78
	 *            subscription
79
	 * @return subscription id (potentially changed)
80
	 */
81
	private String registerSubscription(final ResourceStateSubscription subscription) {
82
		// TODO: change the dao, and implement a method which finds a given subscription directly.
83
		final Collection<ResourceStateSubscription> similar = subscriptionDao.listSubscriptions(subscription.getPrefix(), subscription.getType(),
84
				subscription.getResourceId());
85
		for (final ResourceStateSubscription r : similar) {
86
			if (r != null && getAddress(subscription.getSubscriberAsEpr()).equals(getAddress(r.getSubscriberAsEpr()))
87
					&& (subscription.getXpath() == r.getXpath() || subscription.getXpath().equals(r.getXpath()))
88
					&& (subscription.getResourceId() == r.getResourceId() || subscription.getResourceId().equals(r.getResourceId())))
89
				return r.getSubscriptionId();
90
		}
91

    
92
		subscriptionDao.addSubscription(subscription);
93

    
94
		return subscription.getSubscriptionId();
95
	}
96

    
97
	/**
98
	 * {@inheritDoc}
99
	 *
100
	 * @see eu.dnetlib.enabling.is.sn.SubscriptionRegistry#unsubscribe(java.lang.String)
101
	 */
102
	@Override
103
	public boolean unsubscribe(final String subId) {
104
		return subscriptionDao.removeSubscription(subId);
105
	}
106

    
107
	/**
108
	 * Obtains the address component of the EPR.
109
	 *
110
	 * TODO: refactor in a utility class.
111
	 *
112
	 * @param epr
113
	 *            endpoint reference
114
	 * @return address contained in the endpoint reference
115
	 */
116
	private Object getAddress(final W3CEndpointReference epr) {
117
		final DOMResult dom = new DOMResult();
118
		epr.writeTo(dom);
119

    
120
		try {
121
			return XPathFactory.newInstance().newXPath().evaluate("//*[local-name() = 'Address']", dom.getNode());
122
		} catch (final XPathExpressionException e) {
123
			throw new IllegalStateException("cannot construct xpath expression", e);
124
		}
125
	}
126

    
127
	/**
128
	 * extract resource type name.
129
	 *
130
	 * @param rest
131
	 *            topic expression without prefix
132
	 * @return tuple containing type name and rest of the prefix
133
	 */
134
	public TopicExpressionMatchResult matchType(final String rest) {
135
		final Perl5Util matcher = new Perl5Util(); // NOPMD
136
		if (!matcher.match("/^(\\*|[a-zA-Z_0-9]*)($|/)(.*)$/", rest))
137
			return null;
138

    
139
		return new TopicExpressionMatchResult(matcher.getMatch().group(1), matcher.getMatch().group(2 + 1));
140
	}
141

    
142
	/**
143
	 * extract resource id.
144
	 *
145
	 * @param rest
146
	 *            topic expression without prefix
147
	 * @return tuple containing type name and rest of the prefix
148
	 */
149
	public TopicExpressionMatchResult matchId(final String rest) {
150
		final Perl5Util matcher = new Perl5Util(); // NOPMD
151
		if (!matcher.match("/^([^/]*)(.*)/", rest))
152
			return null;
153

    
154
		return new TopicExpressionMatchResult(matcher.getMatch().group(1), matcher.getMatch().group(2));
155
	}
156

    
157
	/**
158
	 * return all subscriptions matching a given prefix and a given type. Wildcard subscriptions will match any resource type.
159
	 *
160
	 * @param prefix
161
	 *            prefix
162
	 * @param type
163
	 *            concrete type
164
	 * @param resId
165
	 *            resource identifier
166
	 * @return all matching subscriptions
167
	 */
168
	public Collection<ResourceStateSubscription> listMatchingSubscriptions(final String prefix, final String type, final String resId) {
169
		final Set<ResourceStateSubscription> merged = new HashSet<ResourceStateSubscription>();
170
		merged.addAll(subscriptionDao.listSubscriptions(prefix, type, resId));
171
		merged.addAll(subscriptionDao.listSubscriptions(prefix, type, "*"));
172
		merged.addAll(subscriptionDao.listSubscriptions(prefix, "*", "*"));
173
		return merged;
174
	}
175

    
176
	@Override
177
	protected Collection<String> getAcceptedPrefixes() {
178
		return Arrays.asList(new String[] { ResourceStateSubscription.PREFIX_CREATE, ResourceStateSubscription.PREFIX_DELETE,
179
				ResourceStateSubscription.PREFIX_UPDATE });
180
	}
181

    
182
	public ResourceStateSubscriptionDAO getSubscriptionDao() {
183
		return subscriptionDao;
184
	}
185

    
186
	@Required
187
	public void setSubscriptionDao(final ResourceStateSubscriptionDAO subscriptionDao) {
188
		this.subscriptionDao = subscriptionDao;
189
	}
190

    
191
	public SubscriptionRequestFilter getSubscriptionRequestFilter() {
192
		return subscriptionRequestFilter;
193
	}
194

    
195
	public void setSubscriptionRequestFilter(final SubscriptionRequestFilter subscriptionRequestFilter) {
196
		this.subscriptionRequestFilter = subscriptionRequestFilter;
197
	}
198

    
199
}
(8-8/9)