1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package co.stateful.rest;
31
32 import co.stateful.spi.Locks;
33 import com.rexsl.page.JaxbBundle;
34 import com.rexsl.page.Link;
35 import com.rexsl.page.PageBuilder;
36 import java.io.IOException;
37 import java.net.HttpURLConnection;
38 import java.util.Map;
39 import java.util.logging.Level;
40 import javax.ws.rs.Consumes;
41 import javax.ws.rs.DefaultValue;
42 import javax.ws.rs.FormParam;
43 import javax.ws.rs.GET;
44 import javax.ws.rs.POST;
45 import javax.ws.rs.Path;
46 import javax.ws.rs.QueryParam;
47 import javax.ws.rs.WebApplicationException;
48 import javax.ws.rs.core.MediaType;
49 import javax.ws.rs.core.Response;
50 import org.apache.commons.io.IOUtils;
51 import org.apache.commons.lang3.StringUtils;
52
53
54
55
56
57
58
59
60
61 @Path("/k")
62 public final class LocksRs extends BaseRs {
63
64
65
66
67
68 private static final String PARAM = "name";
69
70
71
72
73
74
75 @GET
76 @Path("/")
77 public Response index() throws Exception {
78 return new PageBuilder()
79 .stylesheet("/xsl/locks.xsl")
80 .build(StPage.class)
81 .init(this)
82 .append(
83 new JaxbBundle(
84 "documentation",
85 IOUtils.toString(
86 this.getClass().getResourceAsStream(
87 "doc-locks.html"
88 )
89 )
90 )
91 )
92 .append(this.list())
93 .append(new JaxbBundle("menu", "locks"))
94 .link(new Link("lock", "./lock"))
95 .link(new Link("unlock", "./unlock"))
96 .render()
97 .build();
98 }
99
100
101
102
103
104
105
106
107 @POST
108 @Path("/lock")
109 @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
110 public Response lock(@FormParam(LocksRs.PARAM) final String name,
111 @FormParam("label") @DefaultValue("none") final String label)
112 throws IOException {
113 if (!name.matches("[0-9a-zA-Z\\-\\._\\$]{1,256}")) {
114 throw this.flash().redirect(
115 this.uriInfo().getBaseUriBuilder()
116 .clone()
117 .path(LocksRs.class)
118 .build(),
119 "1-256 letters, numbers or dashes",
120 Level.WARNING
121 );
122 }
123 if (label.isEmpty()) {
124 throw this.flash().redirect(
125 this.uriInfo().getBaseUriBuilder()
126 .clone()
127 .path(LocksRs.class)
128 .build(),
129 "label can't be empty",
130 Level.WARNING
131 );
132 }
133 if (this.user().locks().names().size() > Locks.MAX) {
134 throw this.flash().redirect(
135 this.uriInfo().getBaseUriBuilder()
136 .clone()
137 .path(LocksRs.class)
138 .build(),
139 "too many locks in your account",
140 Level.SEVERE
141 );
142 }
143 final String msg = this.user().locks().lock(name, label);
144 if (msg.isEmpty()) {
145 throw this.flash().redirect(
146 this.uriInfo().getBaseUriBuilder()
147 .clone()
148 .path(LocksRs.class)
149 .build(),
150 String.format("lock %s added successfully", name),
151 Level.INFO
152 );
153 }
154 throw new WebApplicationException(
155 Response.status(HttpURLConnection.HTTP_CONFLICT)
156 .entity(msg)
157 .build()
158 );
159 }
160
161
162
163
164
165
166
167 @GET
168 @Path("/unlock")
169 public void unlock(@QueryParam(LocksRs.PARAM) final String name,
170 @QueryParam("label") final String label)
171 throws IOException {
172 if (StringUtils.isEmpty(label)) {
173 this.user().locks().unlock(name);
174 } else {
175 final String match = this.user().locks().unlock(name, label);
176 if (!match.isEmpty()) {
177 throw new WebApplicationException(
178 Response.status(HttpURLConnection.HTTP_CONFLICT)
179 .entity(String.format("label doesn't match: %s", match))
180 .build()
181 );
182 }
183 }
184 throw this.flash().redirect(
185 this.uriInfo().getBaseUriBuilder()
186 .clone()
187 .path(LocksRs.class)
188 .build(),
189 String.format("%s lock removed", name),
190 Level.INFO
191 );
192 }
193
194
195
196
197
198
199 private JaxbBundle list() throws IOException {
200 return new JaxbBundle("locks").add(
201 new JaxbBundle.Group<Map.Entry<String, String>>(
202 this.user().locks().names().entrySet()
203 ) {
204 @Override
205 public JaxbBundle bundle(final Map.Entry<String, String> ent) {
206 return new JaxbBundle("lock")
207 .add("name", ent.getKey()).up()
208 .add("label", ent.getValue()).up();
209 }
210 }
211 );
212 }
213
214 }