Can Pax Web Whiteboard ResourceMapping have path pointing outside the bundle? - pax

I've tried to register the following ResourceMapping as an OSGi service:
package ru.focusmedia.odp.server.poim.http;
import org.ops4j.pax.web.extender.whiteboard.ResourceMapping;
import ru.focusmedia.odp.server.poim.api.PoimConfig;
import aQute.bnd.annotation.component.Component;
#Component(immediate = true)
public class PoimResourceMapping implements ResourceMapping {
private String httpContextId;
private String alias = "...";
private String someAbsolutePath = "...";
#Override
public String getHttpContextId() {
return httpContextId;
}
#Override
public String getAlias() {
return "/resources";
}
#Override
public String getPath() {
return someAbsolutePath;
}
}
but don't see the result in browser under http://127.0.0.1:8282/resources/aFileUnderMyPath. I can see that Pax Web is accessing my mapping in the log. Is this possible or do I need to write a servlet instead?

The short answer is no.
The path is resolved relative to the bundle and therefore your absolute path will turn in some url syntax error.
Why not loading your file in a bundle and serving it from there? We dynamically load bundles with resources that we need to serve following some versioning conventions.

Related

ServiceStack: Routes.AddFromAssembly still uses /json/reply path and no URL-niceness for properties

I have a ServiceStack self-hosted webservice, using the AppSelfHostBase.
WHen the Configure method is executed, I have this:
public override void Configure(Container container)
{
Config.RouteNamingConventions = new List<RouteNamingConventionDelegate> {
RouteNamingConvention.WithRequestDtoName,
RouteNamingConvention.WithMatchingAttributes,
RouteNamingConvention.WithMatchingPropertyNames,
};
Routes.AddFromAssembly(typeof(ServiceStackHost).Assembly);
and I expected the following service to be executed under /StartBankIdAuthentication path, but it resides under /json/reply/StartBankIdAuthentication instead.
public class StartBankIdAuthentication : IReturn<StartBankIdAuthenticationResponse>
{
public string IdNbr { get; set; }
}
Also, is there an automatic way to make the properties in the DTO to be under "sub-paths", like /StartBankIdAuthentication/1234 instead of the /StartBankIdAuthentication?IdNbr=1234?
I know I can manually add the Route attribute, but it seems cumbersome and also messy in many ways (not Typed, error-prone etc).
I expected the following service to be executed under /StartBankIdAuthentication path, but it resides under /json/reply/StartBankIdAuthentication instead.
The /json/reply/StartBankIdAuthentication is a pre-defined route that's always available by default, they have no relation to Auto Generated Routes.
The default Route generation strategies you've listed are already registered by default and are what's applied when you use Routes.AddFromAssembly(). You should only override with route strategies you want in addition to the defaults, and you should use SetConfig() for any configuration in ServiceStack, e.g:
SetConfig(new HostConfig {
RouteNamingConventions = { MyCustomRouteStrategy }
});
The implementation for the different Route Strategies available in ServiceStack are in RouteNamingConvention.cs, you'll need to register your own strategy for anything additional Route strategies you want.
By default additional routes are generated for any Id or IDs property, the routing docs shows examples of how they can be customized:
The existing rules can be further customized by modifying the related static properties, e.g:
RouteNamingConvention.PropertyNamesToMatch.Add("UniqueId");
RouteNamingConvention.AttributeNamesToMatch.Add("DefaultIdAttribute");
Which will make these request DTOs:
class MyRequest1
{
public UniqueId { get; set;}
}
class MyRequest2
{
[DefaultId]
public CustomId { get; set;}
}
Generate the following routes:
/myrequest1
/myrequest1/{UniqueId}
/myrequest2
/myrequest2/{CustomId}
I know I can manually add the Route attribute, but it seems cumbersome and also messy in many ways (not Typed, error-prone etc).
If you really want you can use nameof() for Typed Routes:
[Route("/" + nameof(StartBankAuthentication) +"/"+ nameof(StartBankAuthentication.IdNbr))]
I'm not sure if Mythz will maybe come up with a different of better solution, but I managed to achieve what I wanted by overriding the GetRouteAttributes, and by using reflection, I could create what I wanted. It looks like this:
public override RouteAttribute[] GetRouteAttributes(Type requestType)
{
string fullname = requestType.FullName.Replace("AlfaOnlineServiceModel.Api.", "");
string path = "/" + fullname.ToLower().Replace(".", "/");
RouteAttribute[] routes = base.GetRouteAttributes(requestType);
if (routes.Length == 0)
{
routes = new RouteAttribute[1];
PropertyInfo[] pInfos = requestType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
foreach(PropertyInfo pi in pInfos)
{
path += "/{" + pi.Name + "}";
}
routes[0] = new RouteAttribute(path);
}
return routes;
}
Which will give for example:
MyMethodResult
The following routes are available for this service:
All Verbs /myCoolPath/mySubPath/myMethod/{MyProperty}

How to override config value from #PropertySource used in a #ConfigurationProperties config class in a unit test using #TestPropertySource

I have a config properties instance with prefix "assets."
#Configuration
#ConfigurationProperties( prefix = "assets", ignoreUnknownFields = true )
public class AssetsProperties
{
#NotNull
private Resource file;
public Resource getFile()
{
return file;
}
public void setFile( Resource file )
{
this.file = file;
}
}
Its default configuration is defined in:
#Configuration
#PropertySource( name = "assetsConfig", value = "classpath:com/package/boot/web/ui/assets/config/default-assets-config.properties" )
#Order( LOW_ORDER )
public class AssetsConfig
{
}
default-assets-config.properties contains:
assets.file=classpath:assets.json
In my unit test I want to override the default value using:
#TestPropertySource( locations = "classpath:com/package/boot/web/ui/assets/tests/assets-config.properties" )
assets-config.properties contains
assets.file=classpath:com/package/boot/web/ui/assets/tests/assets.json
Unfortunately this value is never injected into AssetsProperties.
What do I do wrong, I don't understand because spring fmk ref doc says
Test property sources have higher precedence than those loaded from the operating system’s environment or Java system properties as well as property sources added by the application declaratively via #PropertySource or programmatically.
Thanks in advance,
Paskos
You've hit a limitation in Spring Boot which means that it ignores properties files configured using #TestPropertySource. As an alternative, you can configure one or more inlined properties instead:
#TestPropertySource(properties = "assets.file=classpath:com/package/boot/web/ui/assets/tests/assets.json")

How to attach Sitecore context for controller action mappled to route robots.txt?

In Sitecore I'm trying to set up a way for our client to modify the robots.txt file from the content tree. I am attempting to set up a MVC controller action that is mappled to route "robots.txt" and will return the file contents. My controller looks like this:
public class SeoController : BaseController
{
private readonly IContentService _contentService;
private readonly IPageContext _pageContext;
private readonly IRenderingContext _renderingContext;
public SeoController(IContentService contentService, IPageContext pageContext, IRenderingContext renderingContext, ISitecoreContext glassContext)
: base(glassContext)
{
_contentService = contentService;
_pageContext = pageContext;
_renderingContext = renderingContext;
}
public FileContentResult Robots()
{
string content = string.Empty;
var contentResponse = _contentService.GetRobotsTxtContent();
if (contentResponse.Success && contentResponse.ContentItem != null)
{
content = contentResponse.ContentItem.RobotsText;
}
return File(Encoding.UTF8.GetBytes(content), "text/plain");
}
}
And the route config:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteTable.Routes.MapRoute("Robots.txt", "robots.txt", new { controller = "Seo", action = "Robots" });
}
}
This all works great if I use a route without the ".txt" extension. However after adding the extension I get a null reference exception in the domain layer due to the context database being null. Here's where the error happens:
public Item GetItem(string contentGuid)
{
return Sitecore.Context.Database.GetItem(contentGuid);
}
I'm assuming that there is a setting in sitecore that ignores the .txt extension. I've tried adding it as an allowed extension in the Sitecore.Pipelines.PreprocessRequest.FilterUrlExtensions setting of the config. Is there anything else I could be missing?
Ok, I found the issue. I was correct in assuming that txt needed to be added to the allowed extensions for the Sitecore.Pipelines.PreprocessRequest.FilterUrlExtensions setting. However robots.txt was listed under the IgnoreUrlPrefixes setting in the config file. That was causing sitecore to ignore that request. I removed it from that list and it's working great now.
This is a pure guess, but you might also have to add it to the allowed extensions of Sitecore.Pipelines.HttpRequest.FilterUrlExtensions in httpRequestBegin as well.

How to pass a custom object in REST webservice

i'm having problems transfering a custom object to the client. How can i transfer a custom object to the client and receive it back to the webservice? i'm transferring a file by chunks. i want to know how i should write my client. i tried passing it as MediaType.APPLICATION_JSON in client but i get no result meaning it doesn't get passed back to the webservice. Below is a bit of code im working on.
Webservice
#POST
#Path("/fileTransfer")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public final TransferInfomation transferInfo(final FileModel file)
{
...
}
...(some code)(lets just say a syso)
FileModel Class
public class FileModel {
private String fileID;
private DataHandler dataHandler;
/**
* Constructor.
*/
public FileModel() {
}
(lets assume setters and getters are made)
(Not sure if the webservice is correct). Still learning REST, i want to know how the client should be.
thanks in advance.
A good way to "marshal" and "unmarshal" "custom objects" (in JSON, XML, etc.) in Jersey is to use JAXB (https://jaxb.java.net/).
To do this you need to create a "jaxb class", with the proper getters and setters (and annotations), e.g.:
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class FileModel{
private String fileID;
private DataHandler dataHandler;
public String getFileID(){
return fileID;
}
public void setFileID(String fileID){
this.fileID = fileID;
}
public DataHandler getDataHandler(){
return dataHandler;
}
public void setDataHandler(DataHandler dataHandler){
this.dataHandler = dataHandler;
}
}
Do not forget to declare the #XmlRootElement. Then you can simply declare and use these objects in your API endpoints (methods):
#POST
#Path("/fileTransfer")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public final FileModel transferInfo(FileModel file)
{
// read file in "FileModel" format
// ... make several operations
// return new FileModel (or another format if you will)
}
This should work. Make sure you follow the data structure defined for FileModel correctly in the client side. See here a example on how to handle that in Jersey: How do I POST a Pojo with Jersey Client without manually convert to JSON? (where JAXB is also used).
Your REST endpoint indicates you want to consume and produce JSON. So the REST client needs to send JSON that can be deserialized into FileModel, and the TransferInfomation returned by transferInfo needs to be serialized into JSON to return to the client.
Typically, Java REST frameworks like RESTEasy, Restlet, Camel, and Spring MVC provide facilities that let you define a JSON serializer/deserializer like Jackson and the mapping rules from JSON <--> Java, and the framework handles the details for you.
So if you use one of these frameworks, you will just have to configure them to use the preferred JSON tool and define the rules.
You can achive this like below:
Server Side:
#PUT
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addRecord(CustomClass mCustomClass)
{
///
///
///
return "Added successfully : "+CustomClass.getName();
}// addRecord
Client Side:
public static void main(String[] args)
{
///
///
///
CustomClass mCustomClass = new CustomClass();
Client client = ClientBuilder.newClient();
String strResult = client.target(REST_SERVICE_URL).request(MediaType.APPLICATION_XML).put(Entity.xml(mCustomClass), String.class);
}

Testing Solr via Embedded Server

I'm coding some tests for my solr-indexer application. Following testing best practices, I want to write code self-dependant, just loading the schema.xml and solrconfig.xml and creating a temporary data tree for the indexing-searching tests.
As the application is most written in java, I'm dealing with SolrJ library, but I'm getting problems (well, I'm lost in the universe of corecontainers-coredescriptor-coreconfig-solrcore ...)
Anyone can place here some code to create an Embedded Server that loads the config and also writes to a parameter-pased data-dir?
You can start with the SolrExampleTests which extends SolrExampleTestBase which extends AbstractSolrTestCase .
Also this SampleTest.
Also take a look at this and this threads.
This is an example for a simple test case. solr is the directory that contains your solr configuration files:
import java.io.IOException;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.util.AbstractSolrTestCase;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.SolrParams;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class SolrSearchConfigTest extends AbstractSolrTestCase {
private SolrServer server;
#Override
public String getSchemaFile() {
return "solr/conf/schema.xml";
}
#Override
public String getSolrConfigFile() {
return "solr/conf/solrconfig.xml";
}
#Before
#Override
public void setUp() throws Exception {
super.setUp();
server = new EmbeddedSolrServer(h.getCoreContainer(), h.getCore().getName());
}
#Test
public void testThatNoResultsAreReturned() throws SolrServerException {
SolrParams params = new SolrQuery("text that is not found");
QueryResponse response = server.query(params);
assertEquals(0L, response.getResults().getNumFound());
}
#Test
public void testThatDocumentIsFound() throws SolrServerException, IOException {
SolrInputDocument document = new SolrInputDocument();
document.addField("id", "1");
document.addField("name", "my name");
server.add(document);
server.commit();
SolrParams params = new SolrQuery("name");
QueryResponse response = server.query(params);
assertEquals(1L, response.getResults().getNumFound());
assertEquals("1", response.getResults().get(0).get("id"));
}
}
See this blogpost for more info:Solr Integration Tests
First you need to set your Solr Home Directory which contains solr.xml and conf folder containing solrconfig.xml, schema.xml etc.
After that you can use this simple and basic code for Solrj.
File solrHome = new File("Your/Solr/Home/Dir/");
File configFile = new File(solrHome, "solr.xml");
CoreContainer coreContainer = new CoreContainer(solrHome.toString(), configFile);
SolrServer solrServer = new EmbeddedSolrServer(coreContainer, "Your-Core-Name-in-solr.xml");
SolrQuery query = new SolrQuery("Your Solr Query");
QueryResponse rsp = solrServer.query(query);
SolrDocumentList docs = rsp.getResults();
Iterator<SolrDocument> i = docs.iterator();
while (i.hasNext()) {
System.out.println(i.next().toString());
}
I hope this helps.