Pages

Showing posts with label seam. Show all posts
Showing posts with label seam. Show all posts

Wednesday, July 29, 2009

Summing up the photo gallery (part 6)

I started building a photo gallery app., flickr in mind, as a means to learn the seam framework. To sum up here is my previous posts :
1. "little restfull photo gallery example, with seam, which seams fine", here.
2. "seamy photo gallery, securing it (part 2)", here.
3. "seamy photo gallery, securing it (part 2)", here.
4. "seamy photo gallery, resting easier (part 4)", here.
5. "seamy photo gallery, writing an authentication filter (part 5)", here.
Here is what I did on last couple of days:
Firstly I publish the source code here on google code. Named it jhoto rather hastely. This will probably be an internal name :)
Polished it a little so that it has a original look. I ended up creating some simple bubbley look using paint and some css. Here is how it looks now :
And lastly using jquery to gave the bubbles some animation so that they wiggle a little:
        <script type="text/javascript">
$(document).ready(function() {
var animationLoop = function(it) {
it.animate({
opacity: 0.5 + Math.random() / 2,
marginLeft: (Math.round(Math.random()*10) - 5)+"px",
marginTop: (Math.round(Math.random()*10) - 5)+"px"
}, 1500, "linear",
function() {
animationLoop($(this));
});
};
$("table.aframe").each(function() {
animationLoop($(this));
});
});
</script>
What the above code does is move my bubbles (tables) randomly whit in some threshold.
Will probably do more on this...

Thursday, May 28, 2009

seamy photo gallery, writing an authentication filter (part 5)

On the 4th part I used an simple authentication filter, to secure my urls, that come out of the box. It only supported basic & digest authentication. With basic authentication what you get is a ugly browser dependent box which asks the credentials of the user. On IE its like:
Instead of that I wanted the user to use the login page which I created. In order to do that I first removed basic authentication filter component and started coding mine :
@Scope(APPLICATION)
@Name("customAuthenticationFilter")
@Install(precedence = Install.DEPLOYMENT)
@BypassInterceptors
@Filter(within = "org.jboss.seam.web.exceptionFilter")
public class CustomAuthenticationFilter extends AbstractFilter {
@Logger
protected Log log;

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException("This filter can only process HttpServletRequest requests");
}

HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpRequest.getSession();
Context ctx = new SessionContext(new ServletRequestSessionMap(httpRequest));
Identity identity = (Identity) ctx.get(Identity.class);
if (identity.isLoggedIn()) {
chain.doFilter(request, response);
}
else {
// go login than come back
httpResponse.sendRedirect("/seams/login?serviceUrl="
+ httpRequest.getRequestURL());
}

}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
setUrlPattern("/seam/resource/rest/*");
super.init(filterConfig);
}
}
Seam handles the creation and order of the filters with the help of @Filter annotation. Instead of adding web.xml entries we annotate our filters and specify where they should be in the filter chain. 
I have extended my filter from the seam AbstractFilter class and on the init method I setted the url pattern which the filter mappes to. 
On the doFilter method first thing to do is try to see if the user is already logged in. We have to access the Identity component but seams injections mechanism does not work with the filters. So instead of injecting you have to create a context (and such) to access the Identity component.
Once we have the Identity component we see if the user is already logged in. If so we proceed with the rest of the chain if not we redirect to our custom login page and pass the original page that the user tried to access with a request parameter that I named serviceUrl.
Once the user successfully logs in we need to redirect the user to original resouce pointed by the serviceUrl. To do that I wrote a Listener class which will fire after the user successfully logs in :
@Name("loginRedirect")
@Scope(ScopeType.SESSION)
public class LoginRedirectListener {
private String serviceUrl;

@Observer("org.jboss.seam.security.postAuthenticate")
public void postAuthentication() throws IOException {
if (serviceUrl != null && serviceUrl.length() > 0) {
FacesContext.getCurrentInstance().getExternalContext().redirect(serviceUrl);
}
}
...
The good old observer pattern is implement with annotations on seam. Here using the @Observer annotation we get notified of authentication event end redirect to the serviceUrl if there is one. I have created it as a session bean so that each user will his own serviceUrl. One last thing to do is injecting the serviceUrl parameter to my listener which is done through pages.xml:
<page view-id="/login.xhtml">
<rewrite pattern="/login" />
<param name="serviceUrl" value="#{loginRedirect.serviceUrl}"/>
</page>
Thats all there is...

Wednesday, May 27, 2009

seamy photo gallery, resting easier (part 4)

On my previous post I made a restful photo gallery that has a nice little feature where upon a restful request like 'http://.../mygallery/photo/photo/yoda' served the image named 'yoda' to the user. In order to achive that what I did was first I edited the pages.xml so that it could extract the name (id) part from the request url:
<page view-id="/photo/photo.xhtml" login-required="true">
<rewrite pattern="/photo/photo/{fotoId}" />
<param name="fotoId" value="{fotoId}" />
</page>
Here the name is extracted into el context as 'fotoId'. Second thing is that I created the photo.xhtml mentioned above:
<f:view>
<s:graphicImage id="imaj" value="#{photoService.getFoto(fotoId)}"
style="border: 1px solid black;" lt="image could not be found">
</s:graphicImage>
</f:view>
I used a 's:graphicImage' component which serves the photo using a little service bean using our 'fotoId' parameter. For completeness here is the service bean:
@Name("photoService")
@Scope(ScopeType.APPLICATION)
public class PhotoService {
public byte[] getFoto(String fotoId) {
Photo p = getPhoto(fotoId);
if (p == null) {
return null;
}
return p.getData();
}
...
What bugged me on this method was that although I just wanted the serve the image here I was actually serving a html page which linked to the image. 
After looking around I learned that what I wanted, came with the seam & resteasy integration. RESTEasy (jaxrs) comes with annotations where you can use to serve the result of methods & beans directly. Seam package comes with the required jars  (dont go downloading for a resteasy package from jboss.org) for integrating it. You just put them in your classpath. So inorder to serve my 'photoService.getFoto' I can use the @Get, @Path and other annotations :
@Name("photoService")
@Scope(ScopeType.APPLICATION)
@Path("/foto")
public class PhotoService {

@GET
@Path("/{fotoId}")
@ProduceMime("image/jpeg")

public byte[] getFoto(@PathParam("fotoId")
String fotoId) {
...
Rest access is by default configured under '.../seam/resource/rest/'. So by using the path and get annotions I can access the 'photoService.getFoto' method with the '.../seam/resource/rest/foto/yoda' request. ProduceMime annotation tells that we are serving an jpeg image and the PathParam annotation is used so that we can get the 'yoda' out of the request and used it as a parameter to our method.
Now are we done ? No, because we need to secure it!. We dont want Vader to see the comprimising images of the Yoda (hehe) so we need to secure it with the restrict annotation :
@GET
@Path("/{fotoId}")
@ProduceMime("image/jpeg")
@Restrict("#{s:hasRole('user') || s:hasRole('admin')}")
public byte[] getFoto(@PathParam("fotoId")
String fotoId) ...
Now what happens when someone not logged in tries to acces the yoda is we will see a NotLoggedInException on the stacktrace. Of course we need to handle that and give the user the option to log in. Seam has a 'web:authentication-filter' component that supports the http basic or digest authentication which I configured like this on components.xml:
<web:authentication-filter url-pattern="/seam/resource/rest/*"
auth-type="basic" />
Now the yoda is safe I guess and I am sure there will be different authentication types supported in the future. I got few that could be implemented like the oauth which I belive is used by twitter and such.

Monday, May 4, 2009

seamy photo gallery, securing it (part 3)

Previous part was about setting up security so that we have a admin user that could approve photos and an ordinary one. We have done basic authentication on this part we will look at handling authorization exceptions, securing our view and code based on authority.
First thing I did was adding approve and disapprove (delete the photo) methods to my photoService and make them authorized :
@Restrict("#{s:hasRole('admin')}")
public void approvePhoto(Photo p) {
logger.info("approving :" + p.getName());
p.approve();
entityManager.merge(p);
}

@Restrict("#{s:hasRole('admin')}")
public void disapprovePhoto(Photo p) {
logger.info("disapprovePhoto :" + p.getName());
entityManager.remove(getPhoto(p.getName()));
}
@Restrict annotation with the specified EL makes sure that user invoking these methods have admin right. Upon unauthorized access an authorization exception thrown which I handled in the pages.xml :
<exception class="org.jboss.seam.security.AuthorizationException">
<redirect>
<message>You don't have permission to do this</message>
</redirect>
</exception>
By doing so we will have an authorization exception message on our message component. There is a small issue I encountered however ; http://www.seamframework.org/Community/ExceptionInRedirectHandler
Next I added links to pics in the gallery that will invoke these actions. Seam has annatotions just for doing this kind of stuff on datatables. This is what my datatable looked liked : 
<rich:dataGrid value="#{photoService.frontPagePhotos}" var="foto"
columns="3" elements="9">
<h:column>
<s:div>
<s:link id="lnk_foto" view="/photo/photo.xhtml" propagation="none">
<f:param name="fotoId" value="#{foto.name}" />
<s:graphicImage id="imaj" value="#{foto.data}"
style="border: 1px solid black;" lt="image could not be found">
<s:transformImageSize width="100" maintainRatio="true" />
</s:graphicImage>
</s:link>
</s:div>
<s:div>
<h:outputLabel value="#{foto.name}" for="imaj" />
</s:div>
<s:div>
<h:commandLink value="Approve"
action="#{photoService.approvePhoto(foto)}"
rendered="#{foto.approved eq null}" />
<h:outputText value="," rendered="#{foto.approved eq null}" />
<h:commandLink value="Delete"
action="#{photoService.disapprovePhoto(foto)}"
rendered="#{s:hasRole('admin')}" />
</s:div>
</h:column>
</rich:dataGrid>
Approve and delete buttons must be rendered only with the user with the role admin #{s:hasRole('admin')} EL does that. 
The last thing I did was adding a logout method which is just :
<h:commandButton action="#{identity.logout()}" value="Logout" />

Again I encountered a litte problem with this : http://www.seamframework.org/Community/RedirectingWhereLeftAfterLogout . Thats all for now. Keep coding...

Wednesday, April 29, 2009

seamy photo gallery, securing it (part 2)

The new requirement that I wanted was "photos should be approved before being shown in the main page". For that I needed users with approving privledge or role, therefore it was time to dive in to the seam security part. I used simplest User and Role classes that I could think them and configured them :
<security:jpa-identity-store
user-class="domain.User"
role-class="domain.Role" />
Seam requires that you annotate your user domain and specify your username, password and other fields with annotations. Simplest model mostly from the reference :
public class User {
@Id
@UserPrincipal
private String login;

@UserPassword
private String password;

@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "USER_ROLE")
@UserRoles
private Set<Role> roles = new HashSet<Role>();
...
@Entity
public class Role {
@Id
@RoleName
private String name;
...
@Password annotation has the option to hash the data behind the scenes if you wish so. 
The next thing to the is authenticator method.
<security:identity
authenticate-method="#{authenticator.authenticate}" />
@Scope(ScopeType.APPLICATION)
@Name("authenticator")
public class Authenticator {

@In
EntityManager entityManager;

@In
Identity identity;

public boolean authenticate() {
try {
User u = (User) entityManager.createQuery(
"select u from User u where "
+ "u.login = #{credentials.username} and u.password = #{credentials.password}")
.getSingleResult();

CollectionUtils.forAllDo(u.getRoles(), new Closure() {
public void execute(Object objrole) {
Role role = (Role) objrole;
identity.addRole(role.getName());
}
});

return true;
}
catch (NoResultException ex) {
return false;
}
}

@Create
public void initTestUsers() {
entityManager.persist(new User("mca", "mca").addRole(new Role("user")));
entityManager.persist(new User("op", "op").addRole(new Role("admin")));
}

}
Authenticate method returns if the authentication is successfull or not. Credentials component is used to hold username and password of the user trying to login. If the authentication is successfull we also need to add the user roles to identity component. I have also used the @Create annotation so that I could create some test users when the component is created. The login page needs to get the values to credentials compenent and simply call the Identity.login method :
<h:inputText id="txt_username"
value="#{credentials.username}" ...
<h:inputSecret id="password"
value="#{credentials.password}" ...
<h:commandButton value="Login" action="#{identity.login}"/>
Up to this value we have done what is needed to authenticate a user. To secure our main page we need to mark it a page which needs login on pages.xml :
<pages login-view-id="/login.xhtml" ...

<page view-id="/photo/index.xhtml" login-required="true">
<rewrite pattern="/photo/index" />
</page>
Now when a user logs in he will be redirected to the login page specified in the pages tag. When the login is successfull to redirect the user to their original target page you can use the redirect component with the events:
<event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}" />
</event>

<event type="org.jboss.seam.security.postAuthenticate">
<action execute="#{redirect.returnToCapturedView}" />
</event>
Will be continued...

Wednesday, April 22, 2009

more notes on seam

Here is more tips on seam;

  • I found out that blog example I wrote about has a little bit of history behind it. Apparently it was developed in response to a blog of Simon Brown. He publishes a set of blogger application requirements to compare web frameworks. Although Gavin argues that a blogger application hardly JEE, he still developes the example. Read more on it here http://relation.to/Bloggers/ComparingWebFrameworksSeam .
  • To validate against hibernate validators with JSF only thing you have to do is wrap the components with the "s:validateAll" tag. So if we have a name field in our page like;
    <s:validateAll>
    <h:inputText id="txt_name" value="#{newPhoto.name}" required="true" />
    </s:validateAll>
    Which is annotated like;
    @Length(max = 20)
    String name;
    "s:validateAll" tag will make sure nothing more than 20 characthers will pass and if tries to, a jsf error message will be raised. We could do all the basic validations but still not the required validation because, JSF does not call the validators if there is no value present...
  • Really liked the jboss el. You can even use it with ejb-ql's. Which is useful with standard seam components like the credentials component. This example is to check the credentials of a user: 
    User u = (User) entityManager.createQuery(
    "select u from User u where "
    + "u.login = #{credentials.username} and u.password = #{credentials.password}")
    .getSingleResult();
    A little DSL and we don't have to do more method chaining to pass the parameters. You can also use the EL with some  security annotations and possibly on more places...
  • It's not beans with seam. They call it components. And you can configure, add and remove them through components.xml . You can inject variables to your components with out the scope limit. And components can outject variables.
  • To configure components seam people chose to make a API of annotations instead of defining interfaces like the spring api has. I am not sure if the spring api has something new but this is what you used do if you needed a method to run after a bean has been created;
    public class SomeBean implements InitializingBean, ... {
    ...
    public void afterPropertiesSet() throws Exception {
    ...
    That is you have to implement the InitializingBean interface. On seam you have to annotate a method with the @Create annotation:
    @Create
    public void initTestUsers() {
    ...
    I personally liked the annotation approach better in a language perspective, but I guess its harder to implement and will depend on reflection api, so will be slower.
  • Another thing was my toy app was not working yesterday. It turned out that it could not access the dtd's in xml definitions because jboss was on maintainnance so I deleted them from xml's...

Wednesday, April 15, 2009

little restfull photo gallery example, with seam, which seams fine

I have decided it was time to check out the seam framework since many of my friends gave it a thumbs up. Starting with the blog example that comes with the package I have built a little restfull image gallery. Which looked liked this;
I started with a simple Photo domain object which has name as the id, and the data byte array as the photo file:
@Entity
@Name("newPhoto")
@Scope(ScopeType.SESSION)
public class Photo {
@Id
String name;
byte[] data;
...
}
Whats seamy here is the @Name and the @Scope annotations. @Name annotation declares named beans of the type. In this case I created one to use to hold the user submitted information. @Scope annotation declares the scope of the bean in this case I will use one bean for every session.
Then the action methods to persist and retrieve the photos:
@Name("pholog")
public class PhotoService {
@In
private EntityManager entityManager;
public List<Photo> getFrontPagePhotos() {
...
public byte[] getFoto(String fotoId) {
...
public void createNewPhoto(Photo p) {
if (p.getData() != null) {
try {
entityManager.persist(p);
}
catch (PersistenceException pe) {
FacesMessages.instance().add("Data exists!");
}
}
else {
FacesMessages.instance().add("File is required!");
}
}
}
The @In annotation injects other beans to our new bean. On the createNewPhoto method I do some validations and add validation errors if needed which I suppose a declarative syntax might exist that I don't know about.
On the view side seams comes with some extra tags that standard JSF does not have like the, fileUpload tag, or a better integrated graphicImage compenent. Nevertheless I had to add the richfaces package since I didn't like the way the standard datatable looks and behaves.
My form to upload a Photo:
    <h:form enctype="multipart/form-data">
<span class="errors"><h:messages /></span>
<h:panelGrid columns="2">
<h:outputLabel value="Name" for="txt_name" />
<h:inputText id="txt_name" value="#{newPhoto.name}" required="true"
requiredMessage="Name is required!" />
<h:outputLabel value="File" for="flu_dosya" />
<s:fileUpload id="flu_dosya" data="#{newPhoto.data}" />
</h:panelGrid>
<h:commandButton action="#{pholog.createNewPhoto(newPhoto)}"
value="Create" />
</h:form>
Tricky thing about the fileUpload was that you have to mark your form as "multipart/form-data" otherwise nothing gets uploaded. I am not sure if this is just a seam thing or a JSF spec thing. Another important feature here is #{pholog.createNewPhoto(newPhoto)} el expression which glues the domain and the action beans we have declared. I used to do something similiar using ognl expressions now it comes out of the box. 
Part that displays the gallery:
    <rich:dataGrid value="#{pholog.frontPagePhotos}" var="foto" columns="3"
elements="9">
<h:column>
<s:div>
<s:link id="lnk_foto" view="/photo/photo.xhtml" propagation="none">
<f:param name="fotoId" value="#{foto.name}" />
<s:graphicImage id="imaj" value="#{foto.data}"
style="border: 1px solid black;" lt="image could not be found">
<s:transformImageSize width="100" maintainRatio="true" />
</s:graphicImage>
</s:link>
</s:div>
I used the rich:dataGrid component which gives me nice 3x9 grid to display my images. seams graphicImage component converts the photo byte [] to a resource link which is served. In order it to work you have to add the Seam Resource Servlet to your web.xml;
    <servlet>
<servlet-name>Seam Resource Servlet</servlet-name>
<servlet-class>
org.jboss.seam.servlet.SeamResourceServlet
</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Seam Resource Servlet</servlet-name>
<url-pattern>/seam/resource/*</url-pattern>
</servlet-mapping>
The s:link component links the thumbnail to photo.xml page where the image will be displayed with a restful url. My photo.xhtml is simply like;
<f:view>
<s:graphicImage id="imaj" value="#{pholog.getFoto(fotoId)}"
style="border: 1px solid black;" lt="image could not be found">
</s:graphicImage>
</f:view>
It has a graphicImage component with no transformations and gets the foto with the fotoId parameter. In order this to work and have urls like /examplegallery/photo/photo/yoda to display the image with the name yoda you have to make one last configuration in the pages.xml file;
    <page view-id="/photo/photo.xhtml">
<rewrite pattern="/photo/photo/{fotoId}" />
<param name="fotoId" value="{fotoId}" />
</page>
Here we are creating meaningful urls that could be bookmarked. In this case its like the page, photo, whit the foto name (photo id). And our page used the id (name) to retrieve and serve the image.
And for the last, things that I am curious about seam;
  • pages.xml file had to be seperate files since one file could easily go up to thounds of lines in a real project which won't be manageable. Or there might be a way that dumps the use of that xml file.
  • I am not entirely sure what the component.xml file does...
  • There could be a easier and better way to handle validations and display JSF exception that includes handling hibernate exceptions, validations.
  • Got to check out the seams conversation and navigation features.
  • Do a example with the bijection feature.
Those are my first impressions about seam I have added most of the code I added to the blog example here. So rest of the configuration is there.