Codename One InputStream and OutputStream - codenameone

I'm a bit confused about Storage and FileSystemStorage. I wrote the following methods, but I'm sure that they don't work as expected, because .contains("/") is not enough to distinguish if we are using Storage or FileSystemStorage.
Could you please help me to fix them? Thank you
/**
* Get an InputStream for the given sourceFile, it automatically chooses
* FileSystem API or Storage API
*
* #param sourceFile
* #return
* #throws java.io.IOException
*/
public static InputStream getInputStream(String sourceFile) throws IOException {
if (sourceFile.contains("/")) {
return FileSystemStorage.getInstance().openInputStream(sourceFile);
} else {
// Storage is a flat file system
return Storage.getInstance().createInputStream(sourceFile);
}
}
/**
* Get an OutputStream for the given sourceFile, it automatically chooses
* FileSystem API or Storage API
*
* #param destFile
* #return
* #throws java.io.IOException
*/
public static OutputStream getOutputStream(String destFile) throws IOException {
if (destFile.contains("/")) {
return FileSystemStorage.getInstance().openOutputStream(destFile);
} else {
// Storage is a flat file system
return Storage.getInstance().createOutputStream(destFile);
}
}

Actually they should be pretty good. In theory storage would allow you to use / as part of the file name but honestly it isn't something we've tested and I'm not sure if that's the right thing to do.
FileSystemStorage requires an absolute path and as such will always include a slash character. So this should work fine. Technically a FileSystemStorage path should start with file:// but APIs often work without it to make native code integration easier so that's not a great way to distinguish the API.

Related

AntMedia Native Interface issues

I would like to implement this AntMedia iOS and Android native interface for Codename One:
import com.codename1.system.NativeInterface;
import com.codename1.ui.PeerComponent;
/**
* #deprecated Native Interface, deprecated because you normally should use the
* public API in the AntMediaClient class.
*/
public interface AntMediaNative extends NativeInterface {
/**
* Initializes the connection.
*
* #param serverURL is WebSocket url to connect (wss://...)
* #param streamId is the stream id in the server to process
* #param mode true means MODE_PUBLISH, false means MODE_PLAY
* #param tokenId is one time token string
* #return PeerComponent
*/
public PeerComponent createPeer(String serverURL, String streamId, boolean mode, String tokenId);
/**
* Starts the streaming according to the mode.
*/
public void startStream();
/**
* Stops the streaming.
*/
public void stopStream();
/**
* Switches the cameras.
*/
public void switchCamera();
/**
* Toggle microphone.
*
* #return microphone current status.
*/
public boolean toggleMic();
/**
* Stops the video source.
*/
public void stopVideoSource();
/**
* Starts or restarts the video source.
*/
public void startVideoSource();
/**
* Get the error.
*
* #return error or null if not.
*/
public String getError();
/**
* Camera open order.By default front camera is attempted to be opened at
* first, if it is set to false, another camera that is not front will be
* tried to be open.
*
* #param openFrontCamera if it is true, front camera will tried to be
* opened, if it is false, another camera that is not front will be tried to
* be opened
*/
public void setOpenFrontCamera(boolean openFrontCamera);
}
I need help on two specific issues.
The first problem is how to get the PeerComponent in which to view the live streaming. I don't understand what I have to do in this case in the native Android and iOS code. Could you answer me with an example code for iOS and Android that returns a PeerComponent? Below are the links to the SDKs documentation, I hope it is enough to answer this question.
The second problem is that the SDK for iOS is written in Swift: how do I call the Swift code from a native interface that must be written in Objective-C? Could you answer me with a code example here too?
Thank you for your support.
This is the documentation of the two SDKs:
Android SDK documentation:
https://github.com/ant-media/Ant-Media-Server/wiki/WebRTC-Android-SDK-Documentation
iOS SDK documentation:
https://github.com/ant-media/Ant-Media-Server/wiki/WebRTC-iOS-SDK-Documentation
When you use the Generate Native Interface tool in the IDE it generates matching native code. That code generates native OS methods for each operating system e.g. in the case of Android the createPeer method will return a View.
So for this case you would need to create an instance of org.webrtc.SurfaceViewRenderer and store it in the class (for followup calls of init) then return that from the createPeer method.

Download arbitrary files to Android and iOS cache

I wrote the following method:
/**
* Downloads an arbitrary file to the cache asynchronously, if the current
* platform has a cache path, or to the app home; if the file was previously
* downloaded and if it's still available on the cache, it calls the
* onSuccess callback immediatly.More info:
* https://www.codenameone.com/blog/cache-sorted-properties-preferences-listener.html
*
* #param url The URL to download.
* #param extension you can leave it empty or null, however iOS cannot play
* videos without extension (https://stackoverflow.com/q/49919858)
* #param onSuccess Callback invoked on successful completion (on EDT by
* callSerially).
* #param onFail Callback invoked on failure (on EDT by callSerially).
*/
public static void downloadFileToCache(String url, String extension, SuccessCallback<String> onSuccess, Runnable onFail) {
FileSystemStorage fs = FileSystemStorage.getInstance();
if (extension == null) {
extension = "";
}
if (extension.startsWith(".")) {
extension = extension.substring(1);
}
String name = "cache_" + HashUtilities.sha256hash(url);
if (!extension.isEmpty()) {
name += "." + extension;
}
String filePath;
if (fs.hasCachesDir()) {
// this is supported by Android, iPhone and Javascript
filePath = fs.getCachesDir() + fs.getFileSystemSeparator() + name;
} else {
// The current platform doesn't have a cache path (for example the Simulator)
String homePath = fs.getAppHomePath();
filePath = homePath + fs.getFileSystemSeparator() + name;
}
// Was the file previously downloaded?
if (fs.exists(filePath)) {
CN.callSerially(() -> onSuccess.onSucess(filePath));
} else {
Util.downloadUrlToFileSystemInBackground(url, filePath, (evt) -> {
if (fs.exists(filePath)) {
CN.callSerially(() -> onSuccess.onSucess(filePath));
} else {
CN.callSerially(onFail);
}
});
}
}
It works. It's similar to some methods provided by the Util class, but with two main differences: the first is that the Util class provides methods only to download images to the cache, while I want to download arbitrary files; the second is that I can assume that the same url always returns the same file, so I don't need to download it again if it's still in the cache (while the Util methods always download the files when invoked).
However, I have some doubts.
My first question is about how caching works: currently I'm using this method to download images and videos to cache (in a chatting app), assuming that I don't need to care about when the files will be not more necessary, because the OS will delete them automatically. Is it so, right? Is it possible that the OS deletes files while I'm using them (for example immediately after storing them to the cache), or Android and iOS delete only older files?
I wrote this method to store arbitrary files. Is there any reasonable limit in MB to the file size that we can store in the cache?
Finally, I have a doubt about the callSerially that I used in the method. Previously I didn't use that, but I got odd results: my callbacks do UI manipulations and frequently (but not always) something went wrong. I solved all my callbacks problems adding the callSerially, so callSerially is the solution. But... why? The odd fact is that the ActionListener of Util.downloadUrlToFileSystemInBackground is called under the hood by the addResponseListener(callback) of a ConnectionRequest instance, so the callback is already invoked in the EDT (according to the javadoc). To be sure, I tested CN.isEdt() in the callbacks without adding the callSerially, and it returned true, so in theory callSerially is not necessary, but in practice it is. What's wrong in my reasoning?
Thank you for the explanations.
As far as I know the cache directory is just a directory that the OS is allowed to delete if it needs space. I don't think it will delete it for an active foreground application but that might vary.
There are no limits other than storage. But you still need to consider that the OS won't just clean that directory for you. It will only flush it when storage is very low and even then not always. So you still need to store data responsibly.
I think only the first callSeially has an impact. It defers the result to the next EDT loop instead of continuing in the existing thread.

Is it safe to override Apache CXF's JAXRSBeanValidationInInterceptor to support request-scoped resources?

By default, CXF 3.0.5's JAXRSBeanValidationInInterceptor and JAXRSBeanValidationOutInterceptor do not support validation of arguments passed to request-scoped resource beans. This exclusion is enforced in org.apache.cxf.jaxrs.validation.ValidationUtils.getResourceInstance(Message). Attempting to use a request-scoped resource results in the following warning being logged:
Service object is not a singleton, use a custom invoker to validate
I've spent some time poking around and come up with the following workaround:
/**
* This is a replacement for CXF's builtin
* {#link JAXRSBeanValidationInInterceptor}. This customization supports
* validation of messages handled by non-singleton JAX-RS resource beans. This
* is needed as many of the beans in this project are request-scoped.
*/
public class BeanValidationInInterceptor extends
JAXRSBeanValidationInInterceptor {
/**
* This is a customization of the code in CXF's builtin
* {#link ValidationUtils#getResourceInstance(Message)}.
*
* #see org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor#getServiceObject(org.apache.cxf.message.Message)
*/
#Override
protected Object getServiceObject(Message message) {
final OperationResourceInfo ori = message.getExchange().get(
OperationResourceInfo.class);
if (ori == null) {
return null;
}
if (!ori.getClassResourceInfo().isRoot()) {
return message.getExchange().get(
"org.apache.cxf.service.object.last");
}
final ResourceProvider resourceProvider = ori.getClassResourceInfo()
.getResourceProvider();
return resourceProvider.getInstance(message);
}
}
It seems to work, but as I don't fully understand the reason this wasn't supported in the first place, I'm wondering if it's safe?
Any CXF devs around who can explain if/how I'm shooting myself in the foot here, and what I might do instead?

jcloud : should i keep BlobStoreContext instances?

I've got the following method which allows me to upload files to containers on Rackspace CloudFiles :
/**
* Uploads a file to the storage.
*
* #param f the <code>File</code> which is to be uploaded to the storage.
* #param fileContainer a <code>String</code> representing the container
* which the provided <code>File</code> is to be uploaded to.
* #throws StorageException if an attempt to upload the provided file to
* the storage failed.
*/
public static void upload(File file, String fileContainer) throws StorageException {
if (!file.exists()) {
throw new StorageException("The file '" + file.getName() + "' does not exist.");
}
try {
BlobStoreContext cb = ContextBuilder.newBuilder("cloudfiles-uk")
.credentials(USERNAME, PASSWORD)
.buildView(BlobStoreContext.class);
Blob blob = cb.getBlobStore().blobBuilder(file.getName())
.payload(file)
.build();
cb.getBlobStore().putBlob(fileContainer, blob);
} catch (Exception e) {
throw new StorageException(e);
}
}
Right now, I'm creataing a new context every time the method is called. As far as I understand, the code will only authenticate on first call and from there use a key issued during the first authentication on all subsequent calls. However, I'm not sure it that is correct? Will I be re-authenticating if i throw away the BlobStoreContext instance and instantiate a new one every time upload() is invoked? Would it be a better idea to keep the BlobStoreContext instance?
As you have your code now, you will be reauthenticating on each call to the 'upload' function.
Instead, you'll probably want to create a global context variable, call an authentication function to set your credentials, and then use the context in your upload function.
See this example:
https://github.com/jclouds/jclouds-examples/blob/master/rackspace/src/main/java/org/jclouds/examples/rackspace/Authentication.java

Converting com.google.api.services.drive.model.File to java.io.File

So I want to create a java.io.File so that I can use it to generate a multipart-form POST request. I have the file in the form of a com.google.api.services.drive.model.File so I'm wondering, is there a way I can convert this Google File to a Java File? This is a web-app that uses the Google App Engine SDK, which prohibits every approach I've tried to make this work
No, you it doesn't seem like you can convert from com.google.api.services.drive.model.File to java.io.File. But it should still be possible to generate a multipart-form POST request using your data in Drive.
So the com.google.api.services.drive.model.File class is used for storing metadata about the file. It's not storing the file contents.
If you want to read the contents of your file into memory, this code snippet from the Drive documentation shows how to do it. Once the file is in memory, you can do whatever you want with it.
/**
* Download the content of the given file.
*
* #param service Drive service to use for downloading.
* #param file File metadata object whose content to download.
* #return String representation of file content. String is returned here
* because this app is setup for text/plain files.
* #throws IOException Thrown if the request fails for whatever reason.
*/
private String downloadFileContent(Drive service, File file)
throws IOException {
GenericUrl url = new GenericUrl(file.getDownloadUrl());
HttpResponse response = service.getRequestFactory().buildGetRequest(url)
.execute();
try {
return new Scanner(response.getContent()).useDelimiter("\\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
https://developers.google.com/drive/examples/java
This post might be helpful for making your multi-part POST request from Google AppEngine.
In GoogleDrive Api v3 you can download the file content into your OutputStream. You need for that the file id, which you can get from your com.google.api.services.drive.model.File:
String fileId = "yourFileId";
OutputStream outputStream = new ByteArrayOutputStream();
driveService.files().get(fileId).executeMediaAndDownloadTo(outputStream);

Resources