001    /*
002     * ----------------------------------------------------------------------
003     * Copyright (C) 2009 Enrique Lara (k957@68k.org)
004     *
005     * TinLizard is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU Lesser General Public License
007     * as published by the Free Software Foundation; either version 3.0
008     * of the License, or (at your option) any later version.
009     *
010     * TinLizard is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013     * GNU Lesser General Public License for more details.
014     *
015     * You should have received a copy of the GNU Lesser General Public License
016     * along with TinLizard. If not, see http://www.gnu.org/licenses/.
017     * ----------------------------------------------------------------------
018     */
019    package tinlizard.web;
020    
021    import tinlizard.annotation.security.RolesAllowed;
022    
023    import tinlizard.dataload.DataLoader;
024    
025    import tinlizard.model.Codeline;
026    import tinlizard.model.Dependency;
027    import tinlizard.model.Policy;
028    import tinlizard.model.Project;
029    import tinlizard.model.TinLizard;
030    import tinlizard.model.TinLizardConfig;
031    import tinlizard.model.User;
032    import tinlizard.model.View;
033    
034    import tinlizard.util.JndiUtil;
035    import tinlizard.util.Messages;
036    
037    import java.io.Writer;
038    import java.net.URL;
039    import java.util.Collection;
040    import java.util.Date;
041    import java.util.HashMap;
042    import java.util.Locale;
043    import java.util.Map;
044    
045    import javax.servlet.RequestDispatcher;
046    import javax.servlet.http.HttpSession;
047    
048    import org.apache.log4j.Logger;
049    
050    import org.codehaus.plexus.ContainerConfiguration;
051    import org.codehaus.plexus.DefaultContainerConfiguration;
052    import org.codehaus.plexus.DefaultPlexusContainer;
053    import org.codehaus.plexus.PlexusContainer;
054    import org.codehaus.plexus.PlexusContainerException;
055    import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
056    import org.codehaus.plexus.util.StringUtils;
057    
058    import org.kohsuke.stapler.StaplerRequest;
059    import org.kohsuke.stapler.StaplerResponse;
060    import org.kohsuke.stapler.export.Exported;
061    
062    /**
063     * Root Object for Stapler.
064     *
065     * Misc.
066     * XXX search admin - clear indexes, index some, index all?
067     * XXX security CRUD per object or object group (project+codelines, users, views).
068     * XXX Artifact - get consumers needs to check groupId as well.
069     * XXX bubble up 0001 error in meaningful way to UI.
070     * XXX obtain effective-pom dependencies.
071     * XXX advanced search.
072     *
073     * Model
074     * XXX codeline create/lastmodified confusing - for the record in TL or for the actual codeline? maybe "admins" can set both?
075     * XXX project.getCodelineCount(Policy p);
076     * XXX user.getName();
077     * XXX reconsider changelog/scm interface.
078     * TODO: codeline - a depends on b.
079     * TODO: codeline - merge target
080     * TODO: reporting/summary - codeline/merge plan based on codeline merge target and inter-dependencies.
081     * TODO: codeline search - "bachelors" - no one depends on it?
082     *
083     * Activities:
084     * TODO: activity log for all events: Activity: EntityType,ID,Action,Actor,TimeStamp,IP.
085     *
086     * UI:
087     * XXX: scm connection in codelines.tag
088     * TODO: edit items "in place", instead of having to go to configure screen.
089     * TODO: "logan's run" age of codelines/views on UI. max age? configurable?
090     * TODO: ICONS: http://tango.freedesktop.org/Tango_Desktop_Project
091     * TODO: List out mvn scm:checkout commands to get all projects in view.
092     * TODO: implement HttpDeletable for applicable classes.
093     *
094     * Eclipse Integration
095     * TODO: eclipse - plugin to get codeline policy? right-click -> Team -> getCodelinePolicy().
096     *
097     * Hudson Integration:
098     * TODO: include sample scripts to show updating TinLizard from Hudson/Redmine, etc.
099     */
100    public final class TinLizardWeb extends ObjectWeb<TinLizard> {
101        private static final String PLEXUS_CONFIG = "/META-INF/plexus/tinlizard.xml";
102        private static final TinLizardWeb INSTANCE = new TinLizardWeb();
103        private static final Logger LOG = Logger.getLogger(TinLizardWeb.class);
104        private PlexusContainer plexus;
105    
106        private TinLizardWeb() {
107        }
108    
109        protected TinLizard getSubject() {
110            return TinLizard.getInstance();
111        }
112    
113        public String getName() {
114            return TinLizard.getInstance().getName();
115        }
116    
117        public static TinLizardWeb getInstance() {
118            return INSTANCE;
119        }
120    
121        @Exported
122        public Date getCreated() {
123            return TinLizard.getInstance().getCreated();
124        }
125    
126        @Exported
127        public String getCreatedBy() {
128            return TinLizard.getInstance().getCreatedBy();
129        }
130    
131        @Exported
132        public Date getLastModified() {
133            return TinLizard.getInstance().getLastModified();
134        }
135    
136        @Exported
137        public String getLastModifiedBy() {
138            return TinLizard.getInstance().getLastModifiedBy();
139        }
140    
141        @Exported
142        public String getVersion() {
143            return TinLizard.getInstance().getVersion();
144        }
145    
146        public void startup(String home) {
147            try {
148                ContainerConfiguration c = new DefaultContainerConfiguration();
149                Map<Object, Object> context = new HashMap<Object, Object>();
150                context.putAll(JndiUtil.getContextMap());
151                context.put("TINLIZARD_HOME", home);
152                c.setContext(context);
153    
154                URL configuration = getClass().getResource(PLEXUS_CONFIG);
155                c.setContainerConfigurationURL(configuration);
156    
157                plexus = new DefaultPlexusContainer(c);
158    
159                TinLizardConfig tlconfig = (TinLizardConfig) plexus.lookup(TinLizardConfig.ROLE);
160                TinLizard.initialize(tlconfig);
161    
162                if (JndiUtil.getBoolean(JndiUtil.LOADTESTDATA)) {
163                    LOG.debug("Loading Test Data.");
164                    new DataLoader().run();
165                }
166            } catch (PlexusContainerException e) {
167                throw new IllegalStateException(Messages.error_0500(), e);
168            } catch (ComponentLookupException e) {
169                throw new IllegalStateException(Messages.error_0501(), e);
170            }
171        }
172    
173        public void shutdown() {
174            plexus.dispose();
175        }
176    
177        public ProjectCollectionWeb getAllProjects() {
178            Collection<Project> allProjects = Project.findAll();
179    
180            if (allProjects != null) {
181                return new ProjectCollectionWeb(allProjects, Messages._AllProjects());
182            } else {
183                return null;
184            }
185        }
186    
187        public ProjectWeb getProject(final String name) {
188            if (LOG.isDebugEnabled()) {
189                LOG.debug(name);
190            }
191    
192            Project p = Project.findByName(name);
193    
194            if (p != null) {
195                return new ProjectWeb(p);
196            } else {
197                return null;
198            }
199        }
200    
201        public ViewWeb getDefaultView() {
202            return new ViewWeb(View.getDefaultView());
203        }
204    
205        public ViewCollectionWeb getAllViews() {
206            Collection<View> allViews = View.findAll();
207    
208            if (allViews != null) {
209                return new ViewCollectionWeb(allViews, Messages._AllViews());
210            } else {
211                return null;
212            }
213        }
214    
215        public ViewWeb getView(final String name) {
216            View view = View.findByName(name);
217    
218            if (view != null) {
219                return new ViewWeb(view);
220            } else {
221                return null;
222            }
223        }
224    
225        public CodelineCollectionWeb getAllCodelines() {
226            Collection<Codeline> allCodelines = Codeline.findAll();
227    
228            if (allCodelines != null) {
229                return new CodelineCollectionWeb(allCodelines, Messages._AllCodelines());
230            } else {
231                return null;
232            }
233        }
234    
235        public CodelineCollectionWeb getActiveCodelines() {
236            Collection<Codeline> allCodelines = Codeline.findAllActive();
237    
238            if (allCodelines != null) {
239                return new CodelineCollectionWeb(allCodelines, Messages._ActiveCodelines());
240            } else {
241                return null;
242            }
243        }
244    
245        public CodelineCollectionWeb getRecentlyModified() {
246            Collection<Codeline> allCodelines = Codeline.findRecentlyModified(5); //XXX days ago as user input.
247    
248            if (allCodelines != null) {
249                return new CodelineCollectionWeb(allCodelines, Messages._RecentlyModifiedCodelines());
250            } else {
251                return null;
252            }
253        }
254    
255        public UserCollectionWeb getAllUsers() {
256            Collection<User> allUsers = User.findAll();
257    
258            if (allUsers != null) {
259                return new UserCollectionWeb(allUsers, Messages._AllUsers());
260            } else {
261                return null;
262            }
263        }
264    
265        public UserWeb getUser(final String name) {
266            User user = User.getUserByName(name, false);
267    
268            if (user != null) {
269                return new UserWeb(user);
270            } else {
271                return null;
272            }
273        }
274    
275        @RolesAllowed(RoleNames.USER)
276        public void doPreferences(final StaplerRequest request, final StaplerResponse response) throws Exception {
277            String username = request.getUserPrincipal().getName();
278    
279            User.getUserByName(username, true); //Auto-create user if not exist.
280    
281            response.sendRedirect(request.getContextPath() + "/user/" + username + "/configure");
282        }
283    
284        public void applyTemplate(final Object it, final String template, final Writer out, final Locale locale, final String username) {
285            getSubject().getMashDao().applyTemplate(it, template, out, Locale.getDefault(), username);
286        }
287    
288        public PolicyWeb getDefaultPolicy() {
289            return new PolicyWeb(Policy.getDefaultPolicy());
290        }
291    
292        public PolicyCollectionWeb getAllPolicies() {
293            Collection<Policy> allPolicies = Policy.findAll();
294    
295            if (allPolicies != null) {
296                return new PolicyCollectionWeb(allPolicies, Messages._AllPolicies());
297            } else {
298                return null;
299            }
300        }
301    
302        public PolicyWeb getPolicy(final String name) {
303            Policy policy = Policy.findByName(name);
304    
305            if (policy != null) {
306                return new PolicyWeb(policy);
307            } else {
308                return null;
309            }
310        }
311    
312        public DependencyWeb getDependency(final String groupId) {
313            if (StringUtils.isNotBlank(groupId)) {
314                Dependency dependency = new Dependency();
315                dependency.setGroupId(groupId);
316    
317                return new GroupWeb(dependency);
318            }
319    
320            return null;
321        }
322    
323        @RolesAllowed(RoleNames.USER)
324        public void doAddProject(final StaplerRequest request, final StaplerResponse response) throws Exception {
325            Project p = new Project();
326            request.bindParameters(p, "project.");
327    
328            Codeline c = new Codeline();
329            request.bindParameters(c, "codeline.");
330    
331            User user = new User();
332            request.bindParameters(user, "owner.");
333            user = User.getUserByName(user.getName(), true);
334    
335            boolean updateFromPom = Boolean.parseBoolean(request.getParameter("updateFromPom"));
336    
337            Project project = Project.add(user, p, c, updateFromPom);
338            response.sendRedirect(request.getContextPath() + "/project/" + project.getName());
339        }
340    
341        @RolesAllowed(RoleNames.USER)
342        public void doAddView(final StaplerRequest request, final StaplerResponse response) throws Exception {
343            View view = new View();
344            request.bindParameters(view, "view.");
345    
346            view.add();
347            response.sendRedirect(request.getContextPath() + "/view/" + view.getName());
348        }
349    
350        @RolesAllowed(RoleNames.ADMIN)
351        public void doAddPolicy(final StaplerRequest request, final StaplerResponse response) throws Exception {
352            Policy policy = new Policy();
353            request.bindParameters(policy, "policy.");
354    
355            policy.add();
356            response.sendRedirect(request.getContextPath() + "/policy/" + policy.getName());
357        }
358    
359        /**
360         * Let container managed security handle the rest.
361         */
362        public void doLogin(final StaplerRequest request, final StaplerResponse response) throws Exception {
363            response.sendRedirect(request.getContextPath());
364        }
365    
366        public void doLogout(final StaplerRequest request, final StaplerResponse response) throws Exception {
367            if (LOG.isDebugEnabled() && (request.getUserPrincipal() != null)) {
368                LOG.debug((("doLogout:" + request.getUserPrincipal()) != null) ? request.getUserPrincipal().getName() : "");
369            }
370    
371            HttpSession session = request.getSession(false);
372    
373            if (session != null) {
374                session.invalidate();
375            }
376    
377            response.sendRedirect(request.getContextPath());
378        }
379    
380        public void doSearch(final StaplerRequest request, final StaplerResponse response) throws Exception {
381            String type = request.getParameter("type");
382            String query = request.getParameter("query");
383    
384            final Object results;
385    
386            if ("Dependency".equals(type)) {
387                Collection<Dependency> dependencies = Dependency.search(query);
388                results = new DependencyCollectionWeb(dependencies, Messages._SearchResults(query));
389            } else {
390                Collection<Codeline> codelines = Codeline.search(query);
391                results = new CodelineCollectionWeb(codelines, Messages._SearchResults(query));
392            }
393    
394            request.setAttribute(javax.servlet.jsp.jstl.core.Config.FMT_FALLBACK_LOCALE, response.getLocale());
395    
396            RequestDispatcher view = request.getView(results, "index.jsp");
397            view.include(request, response);
398        }
399    
400        @RolesAllowed(RoleNames.USER)
401        public void doIndexAll(final StaplerRequest request, final StaplerResponse response) throws Exception {
402            getSubject().indexAll();
403    
404            response.sendRedirect(request.getContextPath());
405        }
406    }