View Javadoc
1   /**
2    * Copyright (c) 2014, stateful.co
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the stateful.co nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package co.stateful.rest;
31  
32  import co.stateful.spi.Base;
33  import co.stateful.spi.User;
34  import com.jcabi.manifests.Manifests;
35  import com.jcabi.urn.URN;
36  import com.rexsl.page.BasePage;
37  import com.rexsl.page.BaseResource;
38  import com.rexsl.page.Inset;
39  import com.rexsl.page.JaxbBundle;
40  import com.rexsl.page.Link;
41  import com.rexsl.page.Resource;
42  import com.rexsl.page.auth.AuthInset;
43  import com.rexsl.page.auth.Facebook;
44  import com.rexsl.page.auth.Github;
45  import com.rexsl.page.auth.Google;
46  import com.rexsl.page.auth.Identity;
47  import com.rexsl.page.auth.Provider;
48  import com.rexsl.page.inset.FlashInset;
49  import com.rexsl.page.inset.LinksInset;
50  import com.rexsl.page.inset.VersionInset;
51  import java.io.IOException;
52  import java.net.URI;
53  import java.util.logging.Level;
54  import javax.ws.rs.core.HttpHeaders;
55  import javax.ws.rs.core.MediaType;
56  import javax.ws.rs.core.Response;
57  import org.apache.commons.lang3.Validate;
58  
59  /**
60   * Abstract RESTful resource.
61   *
62   * <p>The class is mutable and NOT thread-safe.
63   *
64   * @author Yegor Bugayenko (yegor@tpc2.com)
65   * @version $Id$
66   * @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
67   */
68  @Resource.Forwarded
69  @Inset.Default(LinksInset.class)
70  @SuppressWarnings("PMD.TooManyMethods")
71  public class BaseRs extends BaseResource {
72  
73      /**
74       * Version of the system, to show in header.
75       */
76      private static final String VERSION_LABEL = String.format(
77          "%s/%s built on %s",
78          // @checkstyle MultipleStringLiterals (3 lines)
79          Manifests.read("Stateful-Version"),
80          Manifests.read("Stateful-Revision"),
81          Manifests.read("Stateful-Date")
82      );
83  
84      /**
85       * Test authentication provider.
86       */
87      private static final Provider TESTER = new Provider() {
88          @Override
89          public Identity identity() {
90              final Identity identity;
91              if ("1234567".equals(Manifests.read("Stateful-Revision"))) {
92                  identity = new Identity.Simple(
93                      URN.create("urn:test:123456"),
94                      "Localhost",
95                      URI.create("http://img.stateful.com/unknown.png")
96                  );
97              } else {
98                  identity = Identity.ANONYMOUS;
99              }
100             return identity;
101         }
102     };
103 
104     /**
105      * Flash.
106      * @return The inset with flash
107      */
108     @Inset.Runtime
109     public final FlashInset flash() {
110         return new FlashInset(this);
111     }
112 
113     /**
114      * Supplementary inset.
115      * @return The inset
116      */
117     @Inset.Runtime
118     public final Inset supplementary() {
119         return new Inset() {
120             @Override
121             public void render(final BasePage<?, ?> page,
122                 final Response.ResponseBuilder builder) {
123                 builder.header("X-Sttc-Version", BaseRs.VERSION_LABEL);
124                 builder.type(MediaType.TEXT_XML);
125                 builder.header(HttpHeaders.VARY, "Cookie");
126             }
127         };
128     }
129 
130     /**
131      * Menu inset.
132      * @return The inset
133      */
134     @Inset.Runtime
135     public final Inset menu() {
136         return new Inset() {
137             @Override
138             public void render(final BasePage<?, ?> page,
139                 final Response.ResponseBuilder builder) {
140                 if (!BaseRs.this.auth().identity().equals(Identity.ANONYMOUS)) {
141                     page.link(new Link("menu:home", "/"));
142                     page.link(new Link("menu:counters", "/counters"));
143                     page.link(new Link("menu:locks", "/k"));
144                 }
145             }
146         };
147     }
148 
149     /**
150      * Token inset.
151      * @return The inset
152      */
153     @Inset.Runtime
154     public final Inset token() {
155         return new Inset() {
156             @Override
157             public void render(final BasePage<?, ?> page,
158                 final Response.ResponseBuilder builder) {
159                 if (!BaseRs.this.auth().identity().equals(Identity.ANONYMOUS)) {
160                     try {
161                         page.append(
162                             new JaxbBundle("token", BaseRs.this.user().token())
163                         );
164                     } catch (final IOException ex) {
165                         throw new IllegalStateException(ex);
166                     }
167                     page.link(new Link("user:refresh", "/u/refresh"));
168                 }
169             }
170         };
171     }
172 
173     /**
174      * Version inset.
175      * @return The inset
176      */
177     @Inset.Runtime
178     public final Inset version() {
179         return new VersionInset(
180             Manifests.read("Stateful-Version"),
181             Manifests.read("Stateful-Revision"),
182             Manifests.read("Stateful-Date")
183         );
184     }
185 
186     /**
187      * Authentication inset.
188      * @return The inset
189      */
190     @Inset.Runtime
191     public final AuthInset auth() {
192         return new AuthInset(this, Manifests.read("Stateful-SecurityKey"))
193             .with(new Auth(this, this.base()))
194             // @checkstyle LineLength (3 lines)
195             .with(new Facebook(this, Manifests.read("Stateful-FbId"), Manifests.read("Stateful-FbSecret")))
196             .with(new Google(this, Manifests.read("Stateful-GoogleId"), Manifests.read("Stateful-GoogleSecret")))
197             .with(new Github(this, Manifests.read("Stateful-GithubId"), Manifests.read("Stateful-GithubSecret")))
198             .with(BaseRs.TESTER);
199     }
200 
201     /**
202      * Get current user.
203      * @return User
204      */
205     protected final User user() {
206         final Identity identity = this.auth().identity();
207         if (identity.equals(Identity.ANONYMOUS)) {
208             throw FlashInset.forward(
209                 this.uriInfo().getBaseUriBuilder().clone()
210                     .path(IndexRs.class)
211                     .build(),
212                 "please login first",
213                 Level.SEVERE
214             );
215         }
216         return this.base().user(identity.urn());
217     }
218 
219     /**
220      * Get spi.
221      * @return The spi
222      */
223     protected final Base base() {
224         final Base base = Base.class.cast(
225             this.servletContext().getAttribute(Base.class.getName())
226         );
227         Validate.notNull(base, "spi is not initialized");
228         return base;
229     }
230 
231 }