1 package pl.matsuo.core.web.controller;
2
3 import org.springframework.beans.factory.annotation.Autowired;
4 import org.springframework.beans.factory.annotation.Value;
5 import org.springframework.http.HttpEntity;
6 import org.springframework.http.HttpHeaders;
7 import org.springframework.transaction.annotation.Transactional;
8 import org.springframework.web.bind.annotation.PathVariable;
9 import org.springframework.web.bind.annotation.RequestBody;
10 import org.springframework.web.bind.annotation.RequestMapping;
11 import org.springframework.web.bind.annotation.RequestParam;
12 import org.springframework.web.bind.annotation.ResponseStatus;
13 import org.springframework.web.util.UriTemplate;
14 import pl.matsuo.core.IQueryRequestParams;
15 import pl.matsuo.core.model.AbstractEntity;
16 import pl.matsuo.core.model.api.Initializer;
17 import pl.matsuo.core.model.query.AbstractQuery;
18 import pl.matsuo.core.model.query.condition.Condition;
19 import pl.matsuo.core.service.db.Database;
20 import pl.matsuo.core.service.facade.IFacadeBuilder;
21
22 import javax.validation.Valid;
23 import java.net.URI;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 import static java.util.Arrays.*;
28 import static java.util.Collections.*;
29 import static org.hibernate.criterion.MatchMode.*;
30 import static org.springframework.http.HttpStatus.*;
31 import static org.springframework.http.MediaType.*;
32 import static org.springframework.web.bind.annotation.RequestMethod.*;
33 import static pl.matsuo.core.model.query.QueryBuilder.*;
34 import static pl.matsuo.core.util.ReflectUtil.*;
35 import static pl.matsuo.core.util.StringUtil.*;
36
37
38 @Transactional
39 public abstract class AbstractController<E extends AbstractEntity, P extends IQueryRequestParams> {
40
41
42 @Autowired
43 protected Database database;
44 @Autowired
45 protected IFacadeBuilder facadeBuilder;
46
47
48 @SuppressWarnings("unchecked")
49 protected final Class<E> entityType = resolveType(getClass(), AbstractController.class, 0);
50
51
52
53
54
55
56 protected List<String> queryMatchers() {
57 return emptyList();
58 }
59
60
61
62
63
64
65
66 protected AbstractQuery<E> listQuery(P params, Condition... additionalConditions) {
67 List<Condition> conditions = new ArrayList<>(asList(additionalConditions));
68
69 if (notEmpty(params.getQuery())) {
70 if (queryMatchers().isEmpty()) {
71 throw new IllegalStateException("When using query parameter queryMatchers must be defined");
72 }
73
74 String[] parts = params.getQuery().split(" ");
75
76 for (String part : parts) {
77 List<Condition> partConditions = new ArrayList<>();
78
79 for (String queryMatcher : queryMatchers()) {
80 partConditions.add(ilike(queryMatcher, part.trim(), ANYWHERE));
81 }
82
83 conditions.add(or(partConditions.toArray(new Condition[0])));
84 }
85 }
86
87 return entityQuery(conditions.toArray(new Condition[0]));
88 }
89
90
91
92
93
94 @RequestMapping(method = GET)
95 public List<E> list(P params) {
96 AbstractQuery<E> query = listQuery(params);
97
98 if (params.getLimit() != null && params.getLimit() > 0) {
99 query.limit(params.getLimit());
100 }
101 if (params.getOffset() != null && params.getOffset() > 0) {
102 query.offset(params.getOffset());
103 }
104
105 List<E> list = database.find(query);
106
107 return list;
108 }
109
110
111
112
113
114 protected List<E> list(Condition... conditions) {
115 return database.find(entityQuery(conditions));
116 }
117
118
119
120
121
122 protected AbstractQuery<E> entityQuery(Condition... conditions) {
123 return query(entityType, conditions).initializer(entityInitializers);
124 }
125
126
127
128
129
130 @RequestMapping(value = "/list/byIds", method = GET, consumes = {APPLICATION_OCTET_STREAM_VALUE})
131 public List<E> listByIds(@RequestParam("ids") List<Integer> ids) {
132 return database.find(entityQuery(in("id", ids)));
133 }
134
135
136
137
138
139 @RequestMapping(value = "/{id}", method = GET)
140 public HttpEntity<E> find(@PathVariable("id") Integer id) {
141 try {
142 return new HttpEntity<E>(database.findById(entityType, id, entityInitializers));
143 } catch (IllegalArgumentException e) {
144 throw new EntityNotFoundException(id);
145 }
146 }
147
148
149 protected final Initializer<E>[] entityInitializers = entityInitializers().toArray(new Initializer[0]);
150
151
152 protected List<? extends Initializer<? super E>> entityInitializers() {
153 return new ArrayList<>();
154 }
155
156
157 @RequestMapping(method = POST, consumes = {APPLICATION_JSON_VALUE})
158 @ResponseStatus(CREATED)
159 public HttpEntity<E> create(@RequestBody @Valid E entity,
160 @Value("#{request.requestURL}") StringBuffer parentUri) {
161 entity = database.create(entity);
162 HttpHeaders headers = new HttpHeaders();
163 headers.setLocation(childLocation(parentUri, entity.getId()));
164 return new HttpEntity<E>(headers);
165 }
166
167
168 @RequestMapping(method = PUT, consumes = {APPLICATION_JSON_VALUE})
169 @ResponseStatus(NO_CONTENT)
170 public void update(@RequestBody @Valid E entity) {
171 database.update(entity);
172 }
173
174
175 @RequestMapping(value = "/{id}", method = DELETE)
176 @ResponseStatus(NO_CONTENT)
177 public void delete(@PathVariable("id") Integer id) {
178 database.delete(entityType, id);
179 }
180
181
182 @RequestMapping(value = "/{id}", method = PUT)
183 @ResponseStatus(NO_CONTENT)
184 public void update(@PathVariable("id") Integer id, @RequestBody E entity) {
185 entity.setId(id);
186 database.create(entity);
187 }
188
189
190 protected URI childLocation(StringBuffer parentUri, Object childId) {
191 UriTemplate uri = new UriTemplate(parentUri.append("/{childId}").toString());
192 return uri.expand(childId);
193 }
194
195
196 protected <E> HttpEntity<E> httpEntity(AbstractEntity entity, StringBuffer parentUri) {
197 HttpHeaders headers = new HttpHeaders();
198 headers.setLocation(childLocation(parentUri, entity.getId()));
199 return new HttpEntity<E>(headers);
200 }
201
202
203 @ResponseStatus(NOT_FOUND)
204 public static class EntityNotFoundException extends RuntimeException {
205 private static final long serialVersionUID = 1L;
206
207
208 public EntityNotFoundException(Integer id) {
209 super("Entity '" + id + "' not found.");
210 }
211 }
212
213
214 public void setDatabase(Database database) {
215 this.database = database;
216 }
217
218 public void setFacadeBuilder(IFacadeBuilder facadeBuilder) {
219 this.facadeBuilder = facadeBuilder;
220 }
221 }
222