when i am using this code it gives error [duplicate] - gwt-rpc

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
when i am using this code it gives error
public class SharedContactServiceImpl extends RemoteServiceServlet implements
SharedContactService {
/**
*
*/
private static final long serialVersionUID = 1L;
public ContactEntry createContact()throws IllegalArgumentException {
// Create the entry to insert
ContactsService myService = new ContactsService("exampleCo-exampleApp-1");
try {
myService.setUserCredentials("abc#in.gappsdemo.in", "xyz#123");
} catch (AuthenticationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String name = "nehaContact";
String notes = "this is some notes from gdata API client";
ContactEntry contact = new ContactEntry();
contact.setTitle(new PlainTextConstruct(name));
contact.setContent(new PlainTextConstruct(notes));
Email primaryMail = new Email();
primaryMail.setAddress("demo#in.gappsdemo.in");
primaryMail.setRel("http://schemas.google.com/g/2005#home");
primaryMail.setPrimary(true);
contact.addEmailAddress(primaryMail);
Email secondaryMail = new Email();
secondaryMail.setAddress("demo#in.gappsdemo.in");
secondaryMail.setRel("http://schemas.google.com/g/2005#work");
secondaryMail.setPrimary(false);
contact.addEmailAddress(secondaryMail);
ExtendedProperty favouriteFlower = new ExtendedProperty();
favouriteFlower.setName("favourite flower");
favouriteFlower.setValue("daisy");
contact.addExtendedProperty(favouriteFlower);
ExtendedProperty sportsProperty = new ExtendedProperty();
sportsProperty.setName("sports");
XmlBlob sportKinds = new XmlBlob();
sportKinds.setBlob(new String("<dance><salsa/><ballroom dancing/><dance/>"));
sportsProperty.setXmlBlob(sportKinds);
contact.addExtendedProperty(sportsProperty);
System.out.println(contact);
// Ask the service to insert the new entry
try{
System.out.println("Inside try Block:");
URL postUrl = new URL("https://www.google.com/m8/feeds/contacts/demo#in.gappsdemo.in/full");
System.out.println("Inside try Block1:");
return myService.insert(postUrl, contact);
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return contact;
}
}
I am using this code on server-side it gives error :
[ERROR] [simplerpc] - Line 9: No source code is available for type com.google.gdata.data.contacts.ContactEntry; did you forget to inherit a required module?

That error is occuring because you are using com.google.gdata.data.contacts.ContactEntry in your client side code. (Its being returned to the client code from your service) Client side objects get compiled into Javascript by GWT. To fix it you need to tell GWT where to find all the source for objects that are converted to Javascript (all client side stuff).
To do that you need to add something like <source path='events'/> in "YourProject.gwt.xml". Below is an example: (Using helloMVP)
1. Created new package 'events' in 'com.hellomvp' (com.hellomvp.events)
2. Added <source path='events'/> to "HelloMVP.gwt.xml Now it looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to="helloMVP">
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<inherits name="com.google.gwt.activity.Activity"/>
<inherits name="com.google.gwt.place.Place"/>
<entry-point class='com.hellomvp.client.HelloMVP'/>
<replace-with class="com.hellomvp.client.ClientFactoryImpl">
<when-type-is class="com.hellomvp.client.ClientFactory"/>
</replace-with>
<!-- Specify the paths for translatable code -->
<source path='client'/>
<source path='shared'/>
<source path='events'/>
</module>
Hope this helps.

Related

How to set EXTRA_PAGE and EXTRA_PAGE_SIZE in a MediaBrowserServiceCompat by getting reference to the Android Auto MediaBrowser?

I have an Android Auto app. I would like to take advantage of pagination for browsing within the app. It seems that you can set EXTRA_PAGE and EXTRA_PAGE_SIZE by getting a reference to the MediaBrowserCompat and passing those constants in .subscribe(). However, I can't figure out how to get a reference to the MediaBrowserCompat that Android Auto Audio uses in order to call .subscribe().
This seems way too complicated for something that should be simple, am I just overthinking things?
How to get the reference to the Android Auto MediaBrowser?
For it, you suppose to know the package name and the class name (if you are trying to bind it outside the app). If you don't know the these details, you can just get it all from the package manager.
final Intent providerIntent =
new Intent(MediaBrowserService.SERVICE_INTERFACE);
List<ResolveInfo> mediaApps =
mPackageManager.queryIntentServices(providerIntent, 0);
for (ResolveInfo info : mediaApps) {
new MediaBrowserCompat(context,
new ComponentName(info.serviceInfo.packageName,
info.serviceInfo.name), mConnectionCallbacks, null);
}
How to set EXTRA_PAGE and EXTRA_PAGE_SIZE?
Bundle bundle = new Bundle();
bundle.putInt(MediaBrowserCompat.EXTRA_PAGE, 1);
bundle.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, 1);
mBrowser.subscribe("__SOME_ID__", bundle, callback);
If you are overriding the onLoadChildren() with the bundle on your service side than you have to handle the paging logic too. You can bypass it by just overriding onLoadChildren without bundle.
Please note: Usually in Android when you see compat suffix at the end, it is new (enhanced) version of one without compat.
MediaActivity is not special Activity, it's a kind of Activity, which is designed to play the musics. And, as you asked MediaBrowserCompat and MediaBrowserServiceCompat, I changed my default architecture (Architecture 2 presented below), to Architecture 1 (Architecture 1 presented below which is new introduced in version 22), just to give the exact answer that you asked.
Two Architectures are:
Architecture1)
1) MediaActivity <--uses----> MediaBrowserCompat <---uses--> MediaServiceBrowserCompat <----> MediaSessionCompat <---> MediaSession <--pass session token --> MediaControllerCompat <-- it also passes token to create --> MediaController /* latest API introduced in 22 */
2)
<service android:name=".MyMediaBrowserServiceCompat"
android:label="#string/service_name" >
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
3) Uses MediaSessionCompat to control music playing.
4) Once a session is created the owner of the session may pass its session token to other processes to allow them to create a MediaControllerCompat to interact with the session.
5) A MediaController can be created if you have a MediaSessionCompat.Token from the session owner.
Now, You created MediaController
From here, both Architecture do the same thing.
Architecture2)
1)MediaActivity <--uses----> MediaBrowser <---uses--> MediaServiceBrowser /* old one introduced in 21. This is default */
2)
<service android:name=".MyMediaBrowserService"
android:label="#string/service_name" >
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
3) Uses MediaSession to control music playing
4) Once a session is created the owner of the session may pass its session token to other processes to allow them to create a MediaController to interact with the session.
5) A MediaController can be created through MediaSessionManager if you hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an enabled notification listener or by getting a MediaSession.Token directly from the session owner.
Now, You created MediaController
From here, both Architecture do the same thing.
Note: By default, when you create Android Auto project, it still uses Architecture 2, But, I'm using Architecture 1 because you asked to do via MediaBrowserCompat. So, you can be a little confused here.
Since exact implementation is kind of long, so I'm providing exact link where you kind find the implementation via MediaBrowserCompat way (Architecture 1)
https://github.com/googlesamples/android-MediaBrowserService
I am posting basic code here based on the Architecture -- because it's a new one introduced in version 22 -- just to enough to show how you can get MediaBrowserCompat reference.
mConnectionCallbacks is the main thing that connectes MediaServiceBrowserCompat with MediaBrowserCompat. MediaBrowserCompat controls the media provided by the MediaServiceBrowserCompat. Activity, which is suitablely designed to control the media is called MediaActivity. MediaActivity uses MediaBrowserCompat to control media (eg, volume, play change etc). MediaBrowserCompat setups mConnectionCallbacks which further has onConnected() etc methods where you can put your own logic there.
public class MyMediaActivity /* can be any activity */ extends AppCompatActivity {
private MediaBrowserCompat mMediaBrowserCompat; /* your reference here */
MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(MyMediaActivity.this);
MediaControllerCompat.Callback controllerCallback =
new MediaControllerCompat.Callback() {
#Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
}
#Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
}
};
private MediaBrowserCompat.ConnectionCallback mConnectionCallbacks =
new MediaBrowserCompat.ConnectionCallback() {
#Override
public void onConnected() {
// Get the token for the MediaSession
MediaSessionCompat.Token token = mMediaBrowserCompat.getSessionToken();
// Create a MediaControllerCompat
MediaControllerCompat mediaController =
null;
try {
mediaController = new MediaControllerCompat(MyMediaActivity.this, // Context
token);
} catch (RemoteException e) {
e.printStackTrace();
}
// Save the controller
MediaControllerCompat.setMediaController(MyMediaActivity.this, mediaController);
// Finish building the UI
buildTransportControls();
}
#Override
public void onConnectionSuspended() {
// The Service has crashed. Disable transport controls until it automatically reconnects
}
#Override
public void onConnectionFailed() {
// The Service has refused our connection
}
};
void buildTransportControls() {
/* you can define your view to control music here */
/* your stuffs here */
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Display the initial state
MediaMetadataCompat metadata = mediaController.getMetadata();
PlaybackStateCompat pbState = mediaController.getPlaybackState();
// Register a Callback to stay in sync
mediaController.registerCallback(controllerCallback);
mConnectionCallbacks = new MediaBrowserCompat.ConnectionCallback();
/* your MediaBrowserCompat instance reference here*/
mMediaBrowserCompat = new MediaBrowserCompat(this,
new ComponentName(this, MyMediaBrowserServiceCompat.class),
mConnectionCallbacks,
null); // optional Bundle
/* now you can call subscribe() callbacks via mMediaBrowserCompat.subscribe(.....) anywhere inside this Activity's
lifecycle callbacks
*/
}
#Override
public void onStart() {
super.onStart();
mMediaBrowserCompat.connect();
}
#Override
public void onStop() {
super.onStop();
// (see "stay in sync with the MediaSession")
if (MediaControllerCompat.getMediaController(MyMediaActivity.this) != null) {
MediaControllerCompat.getMediaController(MyMediaActivity.this).unregisterCallback(controllerCallback);
}
mMediaBrowserCompat.disconnect();
}
}
And, now, you can create MediaBrowserServiceCompat /* note MediaBrowserServiceCompat is service */ as below.
public class MyMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
/* various calbacks here */
}
For more research, you can read this link, which exactly explains the logic I presented above.
https://developer.android.com/guide/topics/media-apps/audio-app/building-a-mediabrowser-client.html#connect-ui-and-mediacontroller

Uploading file in JSF (Need correct file pathway) [duplicate]

This question already has an answer here:
How to upload file using JSF 2.2 <h:inputFile>? Where is the saved File?
(1 answer)
Closed 5 years ago.
I am trying to get my JSF site to upload a picture to the server, but am having a time of it. I've found 4 methodologies to do, but I'd like to use h:InputFile as it seems the most direct.
It would seem I just need to supply the upload path correctly.
After adding #MultipartConfig I no longer get an exception, but I can't verify the file is uploaded or see any error.
public void AddPicture()
{
ConnInfo HitIt = new ConnInfo();
try
{
HitIt.save(fileCelebrityToAdd);
}
catch(Exception ex)
{
//?
}
}
#MultipartConfig(location="C:\\local\\pathway\\Netbeans\\project\\web\\Pictures\\items\\")
public class ConnInfo
{
private String uploadLocation;
public ConnInfo()
{
//uploadLocation = ".\\Pictures\\items\\";
uploadLocation = "C:\\local\\pathway\\Netbeans\\project\\web\\Pictures\\items\\";
}
public boolean TryOut(Part file) throws IOException
{
String monkey = uploadLocation+getFilename(file);
try
{
file.write(monkey);
}
catch(Exception ex)
{
return false;
}
return true;
}
}
Hopefully I've copied the necessary information correctly.
After going back and rereading all the articles I had bookmarked, it was actually the from the one Tam had suggested that I was able to strip out some information.
I didn't need the AJAX, or the #MultipartConfig, and my previous attempt was somehow incorrect, but the follow method allowed me to successfully upload a picture where I wanted it:
public boolean SaveHer(Part file)
{
String monkey = getFilename(file);
try (InputStream input = file.getInputStream())
{
Files.copy(input, new File(uploadLocation, monkey).toPath());
}
catch (IOException e)
{
// Show faces message?
return false;
}
return true;
}

Inconsistent ServiceStack exception handling

I have a simple service built with ServiceStack
public class GetContactMasterDataService : IService<GetContactMasterData>
{
public object Execute(GetContactMasterData getContactMasterData)
{
return ContactApi.FetchContactMasterData();
}
}
In a different namespace:
public class GetContactMasterData
{
}
public class GetContactMasterDataResponse
{
public ResponseStatus ResponseStatus { get; set; }
}
public static GetContactMasterDataResponse FetchContactMasterData()
{
throw new ApplicationException("CRASH");
}
When I send a JSON request I correctly get:
{
"ResponseStatus":{
"ErrorCode":"ApplicationException",
"Message":"CRASH",
}
}
When I send a soap12 request with soapUI, I get the typical yellow screen of death
<html>
<head>
<title>CRASH</title>
...
<h2> <i>CRASH</i> </h2></span>
<b> Description: </b>An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
...
<b> Exception Details: </b>System.ApplicationException: CRASH<br><br>
Is this the expected behavior? How can I get a neatly serialized ResponseStatus similar to the JSON response.
Thanks in advance.
The HTML error page you get doesn't looks like it's coming from ServiceStack, check to see if your website has something that could be hijacking the errors with its own page, e.g: <customErrors />.
The correct behavior for SOAP endpoints is to throw a SOAP fault which if you're using either the Soap11ServiceClient or Soap12ServiceClient generic service clients will be converted to a WebServiceException as seen in this Integration test:
var client = new Soap12ServiceClient(ServiceClientBaseUri);
try
{
var response = client.Send<AlwaysThrowsResponse>(
new AlwaysThrows { Value = TestString });
Assert.Fail("Should throw HTTP errors");
}
catch (WebServiceException webEx)
{
var response = (AlwaysThrowsResponse) webEx.ResponseDto;
var expectedError = AlwaysThrowsService.GetErrorMessage(TestString);
Assert.That(response.ResponseStatus.ErrorCode,
Is.EqualTo(typeof(NotImplementedException).Name));
Assert.That(response.ResponseStatus.Message,
Is.EqualTo(expectedError));
}

RazorEngine v2.1 throwing "Object is null" exception on Razor.Parse

My Template looks like this:
#using RazorEngine;
#model System.String
#{
var content = #"<div id=""text-block-container"">
#Html.Raw(""<h1>test</h2>"")
</div>";
Razor.SetTemplateBase(typeof(MvcTemplateBase<>));
var output = Razor.Parse<MyType>(content, new MyType() );
}
<div>
#Html.Raw(output)
</div>
The MvcTemplateBase looks like:
public abstract class MvcTemplateBase<T> : TemplateBase<T> {
public HtmlHelper<object> Html { get; private set; }
public UrlHelper Url { get; private set; }
public void InitHelpers() {
var httpContext = new HttpContextWrapper(HttpContext.Current);
var handler = httpContext.CurrentHandler as MvcHandler;
if (handler == null)
throw new InvalidOperationException("Unable to run template outside of ASP.NET MVC");
}
Edited title: I figured out what was causing the "Clear" not defined error. I had my own version of TemplateBase defined and that was the version that was namespaced into this. Since I didn't define a Clear method in my implementation that caused the error.
Now I'm getting a null exception on the Razor.Parse call even though the inputs are not null, both 'raw' and the supplied model are initialized and have values.
Edited to correct minor coding error in example.
The MvcTemplateBase<T> type is not yet developed, and the version you are currently using is a very early unfinished version.
I would question why you are trying to run a RazorEngine template within an MVC Razor view?

JSF2 Static Resource Management -- Combined, Compressed

Is anyone aware of a method to dynamically combine/minify all the h:outputStylesheet resources and then combine/minify all h:outputScript resources in the render phase? The comined/minified resource would probably need to be cached with a key based on the combined resource String or something to avoid excessive processing.
If this feature doesn't exist I'd like to work on it. Does anyone have ideas on the best way to implement something like this. A Servlet filter would work I suppose but the filter would have to do more work than necessary -- basically examining the whole rendered output and replacing matches. Implementing something in the render phase seems like it would work better as all of the static resources are available without having to parse the entire output.
Thanks for any suggestions!
Edit: To show that I'm not lazy and will really work on this with some guidance, here is a stub that captures Script Resources name/library and then removes them from the view. As you can see I have some questions about what to do next ... should I make http requests and get the resources to combine, then combine them and save them to the resource cache?
package com.davemaple.jsf.listener;
import java.util.ArrayList;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import org.apache.log4j.Logger;
/**
* A Listener that combines CSS/Javascript Resources
*
* #author David Maple<d#davemaple.com>
*
*/
public class ResourceComboListener implements PhaseListener, SystemEventListener {
private static final long serialVersionUID = -8430945481069344353L;
private static final Logger LOGGER = Logger.getLogger(ResourceComboListener.class);
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
/*
* (non-Javadoc)
* #see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
*/
public void afterPhase(PhaseEvent event) {
FacesContext.getCurrentInstance().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
}
/*
* (non-Javadoc)
* #see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
*/
public void beforePhase(PhaseEvent event) {
//nothing here
}
/*
* (non-Javadoc)
* #see javax.faces.event.SystemEventListener#isListenerForSource(java.lang.Object)
*/
public boolean isListenerForSource(Object source) {
return (source instanceof UIViewRoot);
}
/*
* (non-Javadoc)
* #see javax.faces.event.SystemEventListener#processEvent(javax.faces.event.SystemEvent)
*/
public void processEvent(SystemEvent event) throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot viewRoot = context.getViewRoot();
List<UIComponent> scriptsToRemove = new ArrayList<UIComponent>();
if (!context.isPostback()) {
for (UIComponent component : viewRoot.getComponentResources(context, "head")) {
if (component.getClass().equals(UIOutput.class)) {
UIOutput uiOutput = (UIOutput) component;
if (uiOutput.getRendererType().equals("javax.faces.resource.Script")) {
String library = uiOutput.getAttributes().get("library").toString();
String name = uiOutput.getAttributes().get("name").toString();
// make https requests to get the resources?
// combine then and save to resource cache?
// insert new UIOutput script?
scriptsToRemove.add(component);
}
}
}
for (UIComponent component : scriptsToRemove) {
viewRoot.getComponentResources(context, "head").remove(component);
}
}
}
}
This answer doesn't cover minifying and compression. Minifying of individual CSS/JS resources is better to be delegated to build scripts like YUI Compressor Ant task. Manually doing it on every request is too expensive. Compression (I assume you mean GZIP?) is better to be delegated to the servlet container you're using. Manually doing it is overcomplicated. On Tomcat for example it's a matter of adding a compression="on" attribute to the <Connector> element in /conf/server.xml.
The SystemEventListener is already a good first step (apart from some PhaseListener unnecessity). Next, you'd need to implement a custom ResourceHandler and Resource. That part is not exactly trivial. You'd need to reinvent pretty a lot if you want to be JSF implementation independent.
First, in your SystemEventListener, you'd like to create new UIOutput component representing the combined resource so that you can add it using UIViewRoot#addComponentResource(). You need to set its library attribute to something unique which is understood by your custom resource handler. You need to store the combined resources in an application wide variable along an unique name based on the combination of the resources (a MD5 hash maybe?) and then set this key as name attribute of the component. Storing as an application wide variable has a caching advantage for both the server and the client.
Something like this:
String combinedResourceName = CombinedResourceInfo.createAndPutInCacheIfAbsent(resourceNames);
UIOutput component = new UIOutput();
component.setRendererType(rendererType);
component.getAttributes().put(ATTRIBUTE_RESOURCE_LIBRARY, CombinedResourceHandler.RESOURCE_LIBRARY);
component.getAttributes().put(ATTRIBUTE_RESOURCE_NAME, combinedResourceName + extension);
context.getViewRoot().addComponentResource(context, component, TARGET_HEAD);
Then, in your custom ResourceHandler implementation, you'd need to implement the createResource() method accordingly to create a custom Resource implementation whenever the library matches the desired value:
#Override
public Resource createResource(String resourceName, String libraryName) {
if (RESOURCE_LIBRARY.equals(libraryName)) {
return new CombinedResource(resourceName);
} else {
return super.createResource(resourceName, libraryName);
}
}
The constructor of the custom Resource implementation should grab the combined resource info based on the name:
public CombinedResource(String name) {
setResourceName(name);
setLibraryName(CombinedResourceHandler.RESOURCE_LIBRARY);
setContentType(FacesContext.getCurrentInstance().getExternalContext().getMimeType(name));
this.info = CombinedResourceInfo.getFromCache(name.split("\\.", 2)[0]);
}
This custom Resource implementation must provide a proper getRequestPath() method returning an URI which will then be included in the rendered <script> or <link> element:
#Override
public String getRequestPath() {
FacesContext context = FacesContext.getCurrentInstance();
String path = ResourceHandler.RESOURCE_IDENTIFIER + "/" + getResourceName();
String mapping = getFacesMapping();
path = isPrefixMapping(mapping) ? (mapping + path) : (path + mapping);
return context.getExternalContext().getRequestContextPath()
+ path + "?ln=" + CombinedResourceHandler.RESOURCE_LIBRARY;
}
Now, the HTML rendering part should be fine. It'll look something like this:
<link type="text/css" rel="stylesheet" href="/playground/javax.faces.resource/dd08b105bf94e3a2b6dbbdd3ac7fc3f5.css.xhtml?ln=combined.resource" />
<script type="text/javascript" src="/playground/javax.faces.resource/2886165007ccd8fb65771b75d865f720.js.xhtml?ln=combined.resource"></script>
Next, you have to intercept on combined resource requests made by the browser. That's the hardest part. First, in your custom ResourceHandler implementation, you need to implement the handleResourceRequest() method accordingly:
#Override
public void handleResourceRequest(FacesContext context) throws IOException {
if (RESOURCE_LIBRARY.equals(context.getExternalContext().getRequestParameterMap().get("ln"))) {
streamResource(context, new CombinedResource(getCombinedResourceName(context)));
} else {
super.handleResourceRequest(context);
}
}
Then you have to do the whole lot of work of implementing the other methods of the custom Resource implementation accordingly such as getResponseHeaders() which should return proper caching headers, getInputStream() which should return the InputStreams of the combined resources in a single InputStream and userAgentNeedsUpdate() which should respond properly on caching related requests.
#Override
public Map<String, String> getResponseHeaders() {
Map<String, String> responseHeaders = new HashMap<String, String>(3);
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);
sdf.setTimeZone(TIMEZONE_GMT);
responseHeaders.put(HEADER_LAST_MODIFIED, sdf.format(new Date(info.getLastModified())));
responseHeaders.put(HEADER_EXPIRES, sdf.format(new Date(System.currentTimeMillis() + info.getMaxAge())));
responseHeaders.put(HEADER_ETAG, String.format(FORMAT_ETAG, info.getContentLength(), info.getLastModified()));
return responseHeaders;
}
#Override
public InputStream getInputStream() throws IOException {
return new CombinedResourceInputStream(info.getResources());
}
#Override
public boolean userAgentNeedsUpdate(FacesContext context) {
String ifModifiedSince = context.getExternalContext().getRequestHeaderMap().get(HEADER_IF_MODIFIED_SINCE);
if (ifModifiedSince != null) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);
try {
info.reload();
return info.getLastModified() > sdf.parse(ifModifiedSince).getTime();
} catch (ParseException ignore) {
return true;
}
}
return true;
}
I've here a complete working proof of concept, but it's too much of code to post as a SO answer. The above was just a partial to help you in the right direction. I assume that the missing method/variable/constant declarations are self-explaining enough to write your own, otherwise let me know.
Update: as per the comments, here's how you can collect resources in CombinedResourceInfo:
private synchronized void loadResources(boolean forceReload) {
if (!forceReload && resources != null) {
return;
}
FacesContext context = FacesContext.getCurrentInstance();
ResourceHandler handler = context.getApplication().getResourceHandler();
resources = new LinkedHashSet<Resource>();
contentLength = 0;
lastModified = 0;
for (Entry<String, Set<String>> entry : resourceNames.entrySet()) {
String libraryName = entry.getKey();
for (String resourceName : entry.getValue()) {
Resource resource = handler.createResource(resourceName, libraryName);
resources.add(resource);
try {
URLConnection connection = resource.getURL().openConnection();
contentLength += connection.getContentLength();
long lastModified = connection.getLastModified();
if (lastModified > this.lastModified) {
this.lastModified = lastModified;
}
} catch (IOException ignore) {
// Can't and shouldn't handle it here anyway.
}
}
}
}
(the above method is called by reload() method and by getters depending on one of the properties which are to be set)
And here's how the CombinedResourceInputStream look like:
final class CombinedResourceInputStream extends InputStream {
private List<InputStream> streams;
private Iterator<InputStream> streamIterator;
private InputStream currentStream;
public CombinedResourceInputStream(Set<Resource> resources) throws IOException {
streams = new ArrayList<InputStream>();
for (Resource resource : resources) {
streams.add(resource.getInputStream());
}
streamIterator = streams.iterator();
streamIterator.hasNext(); // We assume it to be always true; CombinedResourceInfo won't be created anyway if it's empty.
currentStream = streamIterator.next();
}
#Override
public int read() throws IOException {
int read = -1;
while ((read = currentStream.read()) == -1) {
if (streamIterator.hasNext()) {
currentStream = streamIterator.next();
} else {
break;
}
}
return read;
}
#Override
public void close() throws IOException {
IOException caught = null;
for (InputStream stream : streams) {
try {
stream.close();
} catch (IOException e) {
if (caught == null) {
caught = e; // Don't throw it yet. We have to continue closing all other streams.
}
}
}
if (caught != null) {
throw caught;
}
}
}
Update 2: a concrete and reuseable solution is available in OmniFaces. See also CombinedResourceHandler showcase page and API documentation for more detail.
You may want to evaluate JAWR before implementing your own solution. I've used it in couple of projects and it was a big success. It used in JSF 1.2 projects but I think it will be easy to extend it to work with JSF 2.0. Just give it a try.
Omnifaces provided CombinedResourceHandler is an excellent utility, but I also love to share about this excellent maven plugin:- resources-optimizer-maven-plugin that can be used to minify/compress js/css files &/or aggregate them into fewer resources during the build time & not dynamically during runtime which makes it a more performant solution, I believe.
Also have a look at this excellent library as well:- webutilities
I have an other solution for JSF 2. Might also rok with JSF 1, but i do not know JSF 1 so i can not say. The Idea works mainly with components from h:head and works also for stylesheets. The result
is always one JavaScript (or Stylesheet) file for a page! It is hard for me to describe but i try.
I overload the standard JSF ScriptRenderer (or StylesheetRenderer) and configure the renderer
for the h:outputScript component in the faces-config.xml.
The new Renderer will now not write anymore the script-Tag but it will collect all resources
in a list. So first resource to be rendered will be first item in the list, the next follows
and so on. After last h:outputScript component ist rendered, you have to render 1 script-Tag
for the JavaScript file on this page. I make this by overloading the h:head renderer.
Now comes the idea:
I register an filter! The filter will look for this 1 script-Tag request. When this request comes,
i will get the list of resources for this page. Now i can fill the response from the list of
resources. The order will be correct, because the JSF rendering put the resources in correct order
into the list. After response is filled, the list should be cleared. Also you can do more
optimizations because you have the code in the filter....
I have code that works superb. My code also can handle browser caching and dynamic script rendering.
If anybody is interested i can share the code.

Resources